小部件不更新视图并且也没有显示错误

Posted

技术标签:

【中文标题】小部件不更新视图并且也没有显示错误【英文标题】:Widget not updating Views and showing no errors as well 【发布时间】:2013-06-26 09:29:19 【问题描述】:

我正在开发一个小部件,它运行时没有任何错误,但我的小部件的 ImageView 没有更新,也没有给出任何错误。因此让我对自己做错了什么感到困惑。这是代码:-

BroadcastReceiver _broadcastReceiver;
GetDrawableResources getDrawableResources = new GetDrawableResources();
private final SimpleDateFormat _sdfWatchTime = new SimpleDateFormat("HH:mm");

@Override
public void onEnabled(final Context context) 
    super.onEnabled(context);
    // TODO Auto-generated method stub
    System.out.println("Entering onstart");
    _broadcastReceiver = new BroadcastReceiver() 
        @Override
        public void onReceive(Context ctx, Intent intent) 
            System.out.println("Registering BroadCast");
            if (intent.getAction().compareTo(Intent.ACTION_TIME_TICK) == 0) 

                System.out.println(_sdfWatchTime.format(new Date()));
                Bundle extras = intent.getExtras();
                if (extras != null) 
                    AppWidgetManager awm = AppWidgetManager
                            .getInstance(context);
                    ComponentName thisWidget = new ComponentName(
                            context.getPackageName(),
                            MainActivity.class.getName());
                    int[] appWidgetIds = awm.getAppWidgetIds(thisWidget);
                    onUpdate(context, awm, appWidgetIds);
                

            
        
    ;
    context.getApplicationContext().registerReceiver(_broadcastReceiver,
            new IntentFilter(Intent.ACTION_TIME_TICK));



@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
        int[] appWidgetIds) 
    // TODO Auto-generated method stub
    super.onUpdate(context, appWidgetManager, appWidgetIds);
    for (int awID : appWidgetIds) 
        updateAppWidget(context, appWidgetManager, awID);
    



private void updateAppWidget(Context context,
        AppWidgetManager appWidgetManager, int awID2) 
    // TODO Auto-generated method stub
    // class to get drawable images that will be used to denote hours and
    // minutes

    int time[] = new int[new GetTime().getTime(context
            .getApplicationContext()).length];
    // class to get each digit of time separately eg 10:10, separating 1 0 1
    // 0 working correctly
    time = new GetTime().getTime(context.getApplicationContext());
    // changing views

    System.out.println("awID for " + awID2);
    RemoteViews views = new RemoteViews(context
            .getPackageName(), R.layout.activity_main);

    views.setImageViewResource(R.id.ivHour0,
            getDrawableResources.setTimeDrawable(time[0]));
    views.setImageViewResource(R.id.ivHour1,
            getDrawableResources.setTimeDrawable(time[1]));
    views.setImageViewResource(R.id.ivMinute0,
            getDrawableResources.setTimeDrawable(time[2]));
    views.setImageViewResource(R.id.ivMinute1,
            getDrawableResources.setTimeDrawable(time[3]));


    System.out.println("updating the views");

    appWidgetManager.updateAppWidget(awID2, views);


我的 LogCat 中的输出

06-28 19:46:00.076: I/System.out(680): Registering BroadCast
06-28 19:46:00.086: I/System.out(680): 19:46
06-28 19:46:00.096: I/System.out(680): awID for 14
06-28 19:46:00.096: I/System.out(680): updating the views
06-28 19:47:00.086: I/System.out(680): Registering BroadCast
06-28 19:47:00.086: I/System.out(680): 19:47
06-28 19:47:00.096: I/System.out(680): awID for 14
06-28 19:47:00.096: I/System.out(680): updating the views

因此,appWidgetID 是正确的,并且函数正在按应有的方式执行,但视图没有更新。它们和以前一样。

【问题讨论】:

【参考方案1】:

您的方法有一个有效的基础,但是您正在 onEnabled 方法中创建和注册您的 broadcastReceiver,而该方法又属于 WidgetProvider 类。

当 WidgetProvider 的方法 (onEnabled, onUpdate) 执行完毕后,WidgetProvider 类将被系统销毁,您的 broadcastReceiver 也将被销毁。如您所见,这里没有错误,但也没有收到。

为了做你想做的事,你需要一个更“稳定”的应用程序组件来保存你的广播接收器,这样的组件就是一个服务。即使您的应用程序停止,服务也会继续运行,因此它是此类作业的好组件。

所以为了回答你的问题,我建议你创建一个服务来创建和注册你的广播接收器,然后你从你的 widgetprovider 类的 onEnabled 方法启动服务,当用户从他的小部件中删除你的小部件时你可以停止服务主屏幕。

从我的建议开始的一个好地方是here...

编辑 Here 是来自 google 的 App Widgets 文档(大约在中间):

注意:由于 AppWidgetProvider 是 BroadcastReceiver 的扩展,因此无法保证您的进程在回调方法返回后继续运行(有关广播生命周期的信息,请参阅 BroadcastReceiver)。如果您的 App Widget 设置过程可能需要几秒钟(可能在执行 Web 请求时)并且您需要继续您的过程,请考虑在 onUpdate() 方法中启动服务。在服务中,您可以对 App Widget 执行自己的更新,而不必担心 AppWidgetProvider 由于应用程序无响应 (ANR) 错误而关闭。有关运行服务的 App Widget 示例,请参阅维基词典示例的 AppWidgetProvider。

我在我的 appwidget 中遇到过类似的问题,结果是我的 appwidgetprovider 的生命周期引起的,所以我基于此回答了你。但是,在您的观察中,您是对的,您的小部件至少应该在第一次更新时更新自身,所以更多地研究它我发现您正在使用自定义 getDrawableResources 类来获取您的资源(您可以用它编辑您的问题吗?代码?)并且您正在使用 getDrawableResources.setTimeDrawable(time[0]));为了得到drawable,也许你应该把它改成getDrawableResources.getTimeDrawable(time[0]))?

编辑 2

这是说明我的建议的代码。首先是我们注册 BatteryReceiver 的服务。

class FullWidgetBatteryChangedReceiver extends BroadcastReceiver 
//----- BroadcastReceiver Overrides -----
@Override
public void onReceive(Context cx, Intent intent) 
    Log.d("BatteryChangedReceiver", "onReceive");

    Commands.FullWidgetUpdate(cx, -1, 0); //<-- Update all widgets

//----- BroadcastReceiver Overrides END -----


public class myWidgetService extends Service 

//----- Private Members -----
private FullWidgetBatteryChangedReceiver    bcRCV;
//----- Private Members END -----



//----- Private Code -----    
private void RegisterBatteryChangedReceiver() 
    Log.d(TAG, "RegisterBatteryChangedReceiver");

    if (bcRCV == null) 
        Log.d(TAG, "BatteryChangedReceiver needs Registration and I am doing it...");

        bcRCV = new FullWidgetBatteryChangedReceiver();
        IntentFilter actions = new IntentFilter();
        actions.addAction(Intent.ACTION_BATTERY_CHANGED);
        registerReceiver(bcRCV, actions);
     else 
        Log.d(TAG, "BatteryChangedReceiver does not needs Registration");
    


private void UnRegisterBatteryChangedReceiver() 
    Log.d(TAG, "UnRegisterBatteryChangedReceiver");

    if (bcRCV != null) 
        Log.d(TAG, "Assuming that BatteryChangedReceiver is registered and I am unregister it...");

        unregisterReceiver(bcRCV);
        bcRCV = null;
     else 
        Log.d(TAG, "Assuming that BatteryChangedReceiver is NOT registered");
    
    
//----- Private Code END -----



//----- Service Overrides -----
@Override
public void onCreate() 
    super.onCreate();


@Override
public int onStartCommand(Intent intent, int flags, int startId) 
    RegisterBatteryChangedReceiver();

    return Service.START_STICKY; //<-- This is important!!!
   

@Override
public IBinder onBind(Intent intent) 
    return null;


@Override
public void onDestroy()        
    UnRegisterBatteryChangedReceiver();

    super.onDestroy();
   
//----- Service Overrides END -----


然后这里是完成服务操作的 myWidgetProvider:

public class myWidgetProvider extends AppWidgetProvider 


//----- AppWidgetProvider Overrides -----
@Override
public void onEnabled(Context context) 
    super.onEnabled(context);


@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) 
    super.onUpdate(context, appWidgetManager, appWidgetIds);

    // If no specific widgets requested, collect list of all
    if (appWidgetIds == null) 
        appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, FullWidgetProvider.class));
    

    // Εδώ κάνουμε update to Widget για κάθε id ξεχωριστά
    for (int widgetId : appWidgetIds) 
        Commands.FullWidgetUpdate(context, widgetId, 0); //<-- Update το widgetId
    

    context.startService(new Intent(context, myWidgetService.class));


@Override
public void onDeleted(Context context, int[] appWidgetIds)         
    super.onDeleted(context, appWidgetIds);


@Override
public void onDisabled(Context context) 
    super.onDisabled(context);

    context.stopService(new Intent(context, FullWidgetService.class));

    int[] appWidgetIds = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, FullWidgetProvider.class));

    if (appWidgetIds.length != 0) 
        AppWidgetHost host = new AppWidgetHost(context, 0);

        for (int widgetId : appWidgetIds) 
            host.deleteAppWidgetId(widgetId);
           
    

//----- AppWidgetProvider Overrides END -----       

希望这会有所帮助...

【讨论】:

我不知道这些WidgetProvider类和broadcastReceiver在执行后被销毁了。我正在阅读有关该服务的信息,如果它解决了我的问题,请将答案标记为正确。谢谢 我想清除一些东西。你说在执行onEnabled之后,onUpdate和broadcastReciever被销毁但是每分钟我的broadcastReciever然后onUpdate然后最后updateAppWidget被调用。因此,如果每分钟调用一次 updateAppWidget,则必须更新视图,因为更新是在此方法中完成的。我说错了吗? getDrawableResources 是我制作的一个自定义类,它没有扩展或实现任何东西。这是一个简单的类。它根据 time[0] 发送返回图像的 id。例如,如果时间 [0] 为 8,则在 getDrawableResources 中运行的代码为案例 8:时间 = R.drawable.time_8;休息; @RohanKandwal 是这样的,但看看我的第二次编辑,我在其中添加了一些代码来说明这种方法。谢谢。 @RohanKandwal Commands 是我创建的一个自定义静态类,目的是让事情井井有条。您可以将此行替换为您的 updatewidget 函数。对于给您带来的不便,我深表歉意,但我的代码仅用于说明目的,而不是完整的工作代码。顺便说一句,我很高兴你解决了你的问题。谢谢。

以上是关于小部件不更新视图并且也没有显示错误的主要内容,如果未能解决你的问题,请参考以下文章

带有集合(ListView)的Android小部件不显示项目

带有 tkinter xgboost 的 cxfreeze 小部件未显示但没有错误

通过 remoteview Android 更新小部件

显示今天视图小部件内容的正确方法

小部件不更新视图

小部件更新的 RemoteViews 超出最大位图内存使用错误