Android 小部件上的线程

问题描述 投票:0回答:2

我正在尝试制作一个小部件时钟。

在我的 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();
}

这会生成一个空白小部件。

我不是要代码,只是帮助我确定我的错误是什么以及我应该如何解决这个问题?

android multithreading android-widget threadpool
2个回答
1
投票

您的

Thread
方法不起作用,因为 Android 不允许未创建
Thread
层次结构的
View
接触这些
View
。与大多数情况一样,您尝试从工作人员
View
更新的
Thread
最有可能在 UI 线程上膨胀并实例化,作为 Android 的
Activity
创建过程的一部分。

我认为你有两个选择:

  1. 始终在

    Activity
    构造函数中传递
    Context
    作为
    NewAppWidget
    参数。

    然后您可以安全地将

    Context
    对象转换为
    Activity
    并调用
    Activity.runOnUiThread(Runnable)
    ,或者将
    Context
    字段更改为
    Activity
    以避免每次都进行转换。

  2. 使用

    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
仍在运行。


0
投票

我知道这是一个老问题,并且已为该帖子的作者解决。

但是,对于未来的读者,我想修改#oaskamay给出的答案(https://stackoverflow.com/a/49520145/13755907)。

  1. 第二种方法使用 Handler,可以解决时钟问题。在本案中这是正确且合理的。

  2. 第一种方法没有用。由于这是系统调用的AppWidgetProvider类(当在屏幕上创建新的Widget时)仅使用默认构造函数,因此在创建新实例时无法提供Activity

  3. 但是,如果有人仍然需要在小部件中使用非 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();
     }
    

    }

© www.soinside.com 2019 - 2024. All rights reserved.