我正在尝试制作一个小部件时钟。
在我的 MainActivity 上,使用 runOnUiThread 完美显示时钟。
由于小部件不扩展“活动”,我无法 runOnUiThread。
但是当我尝试将 Thread 与小部件一起使用时,我收到错误(或)空白小部件。
public class NewAppWidget extends AppWidgetProvider {
static Context cont;
static AppWidgetManager awm;
static int awid;
static Thread t;
static RemoteViews views;
static String timeText;
static String dateText;
public static Bitmap BuildUpdate(String txttime, int size,Context context){
Paint paint= new Paint();
paint.setTextSize(size);
Typeface
custTface=Typeface.createFromAsset(context.getAssets(),"fonts/Lato-Regular.ttf");
paint.setTypeface(custTface);
paint.setColor(Color.WHITE);
paint.setTextAlign(Paint.Align.LEFT);
paint.setSubpixelText(true);
paint.setAntiAlias(true);
// - in the next line is highly important.
float baseline = -paint.ascent();
int width =(int) paint.measureText(txttime+0.5f);
int height= (int) (baseline+paint.descent()+0.5f);
Bitmap image = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(image);
canvas.drawText(txttime,0,baseline,paint);
return image;
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
cont=context;
awm=appWidgetManager;
awid=appWidgetId;
t = new Thread() {
@Override
public void run() {
while (!isInterrupted()){
try {
t.sleep(1000);
new Thread(new Runnable(){
@Override
public void run() {
long date = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
SimpleDateFormat sdf2 = new SimpleDateFormat("hh-mm-ss a");
timeText = sdf2.format(date);
dateText = sdf.format(date);
views.setImageViewBitmap(R.id.txtTime, BuildUpdate(timeText, 100, cont));
views.setImageViewBitmap(R.id.txtDate, BuildUpdate(dateText, 25, cont));
awm.updateAppWidget(awid, views);
}
});
}catch (InterruptedException e) {
}
}
}
};
t.start();
}
这会生成一个空白小部件。
我不是要代码,只是帮助我确定我的错误是什么以及我应该如何解决这个问题?
您的
Thread
方法不起作用,因为 Android 不允许未创建 Thread
层次结构的 View
接触这些 View
。与大多数情况一样,您尝试从工作人员 View
更新的 Thread
最有可能在 UI 线程上膨胀并实例化,作为 Android 的 Activity
创建过程的一部分。
我认为你有两个选择:
始终在
Activity
构造函数中传递 Context
作为 NewAppWidget
参数。
然后您可以安全地将
Context
对象转换为 Activity
并调用 Activity.runOnUiThread(Runnable)
,或者将 Context
字段更改为 Activity
以避免每次都进行转换。Handler
。
按如下方式实例化您的
Handler
。这将创建一个链接到 UI 线程(也称为“主”线程)的 Handler
对象。
Handler handler = new Handler(Looper.getMainLooper());
然后,您可以使用
Handler
发布 Runnable
,然后从您的工作人员 Thread
在 UI Thread
上运行。您可以通过以下方式从您的工作人员 View
更新 Thread
:
handler.post(new Runnable() { ... });
我推荐第二种方法,因为它更干净,而且您并不总是需要这样引用
Activity
。
请记住第二种方法,在您的
Runnable
中,您需要对要更新的 WeakReference
或 Activity
使用 View
以防止内存泄漏。即使您的 Thread
或 Activity
(应该是)被垃圾收集,您的工作线程 View
可能仍在运行。使用 WeakReference
允许垃圾收集器收集您的 Activity
/ View
,即使您的工作线程 Thread
仍在运行。
我知道这是一个老问题,并且已为该帖子的作者解决。
但是,对于未来的读者,我想修改#oaskamay给出的答案(https://stackoverflow.com/a/49520145/13755907)。
第二种方法使用 Handler,可以解决时钟问题。在本案中这是正确且合理的。
第一种方法没有用。由于这是系统调用的AppWidgetProvider类(当在屏幕上创建新的Widget时)仅使用默认构造函数,因此在创建新实例时无法提供Activity
但是,如果有人仍然需要在小部件中使用非 UI 线程,例如从互联网获取数据,Android 禁止在任何 UI 线程上运行,那么上述解决方案是使用...处理程序,但在线程内。这样,您将把数据从非 UI Worker 返回到基于 UI 的 Handler。我用 // New line comment 标记了新的代码片段。我还将方法从静态更改为标准(我不明白这里静态方法的目的)。
public class NewAppWidget extends AppWidgetProvider {
private Context cont;
private AppWidgetManager awm;
private int awid;
private Thread t;
private RemoteViews views;
private String timeText;
private String dateText;
// New line
private Handler handler = new Handler(Looper.getMainLooper());
public Bitmap BuildUpdate(String txttime, int size,Context context){
Paint paint= new Paint();
paint.setTextSize(size);
Typeface
custTface=Typeface.createFromAsset(context.getAssets(),"fonts/Lato-Regular.ttf");
paint.setTypeface(custTface);
paint.setColor(Color.WHITE);
paint.setTextAlign(Paint.Align.LEFT);
paint.setSubpixelText(true);
paint.setAntiAlias(true);
// - in the next line is highly important.
float baseline = -paint.ascent();
int width =(int) paint.measureText(txttime+0.5f);
int height= (int) (baseline+paint.descent()+0.5f);
Bitmap image = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(image);
canvas.drawText(txttime,0,baseline,paint);
return image;
}
void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
cont=context;
awm=appWidgetManager;
awid=appWidgetId;
t = new Thread() {
@Override
public void run() {
while (!isInterrupted()){
try {
t.sleep(1000);
new Thread(new Runnable(){
@Override
public void run() {
long date = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
SimpleDateFormat sdf2 = new SimpleDateFormat("hh-mm-ss a");
timeText = sdf2.format(date);
dateText = sdf.format(date);
// New line
handler.post(new Runnable() {
@Override
public void run() { views.setImageViewBitmap(R.id.txtTime, BuildUpdate(timeText, 100, cont));
views.setImageViewBitmap(R.id.txtDate, BuildUpdate(dateText, 25, cont));
awm.updateAppWidget(awid, views);
}
});
}
});
}catch (InterruptedException e) {
}
}
}
};
t.start();
}
}