防止触发 WidgetProvider 的 onUpdate

Posted

技术标签:

【中文标题】防止触发 WidgetProvider 的 onUpdate【英文标题】:prevent triggering of onUpdate of WidgetProvider 【发布时间】:2012-07-31 15:47:04 【问题描述】:

我正在实现一个具有 ConfigurationActivity 的小部件,并且必须与 Eclair(2.1) 保持兼容。

关于 AppWidgetProviders 的documentation onUpdate 方法明确指出:

... .但是,如果你声明了一个配置Activity,则在用户添加App Widget时不会调用该方法,而是在后续更新时调用该方法。配置完成后执行第一次更新是配置活动的责任。 (请参阅下面的创建应用小部件配置活动。)

不幸的是,这不是真的(至少对于我的带有 JellyBean 的 Nexus S)。事实上,onUpdate 在我的 ConfigurationActivity 触发器中被调用 before onCreate。我想知道,其他手机上是否有类似的行为,是否可以阻止我的 Provider 内部的 onUpdate 调用?

我的解决方法是在我的 WidgetConfigurationActivity 内的 SharedPreferences 中使用特定的 AppWidgetId 存储一个标志。如果它不存在,我可以假设没有首先调用 ConfigurationActivity。这可行,但在我看来真的很难看。如果我不能阻止onUpdate触发,有没有更好的解决方案?

【问题讨论】:

是的,它被调用了。只是另一个不正确的文档 - 甚至 Google 有时也会出错。 我已经看到每次在创建配置活动之前都会调用 onUpdate,无论 android 版本、设备或模拟器如何 【参考方案1】:

是的,当您在主屏幕上添加小部件时会调用 onUpdate()。 见这里:

http://developer.android.com/reference/android/appwidget/AppWidgetProvider.html

http://developer.android.com/reference/android/appwidget/AppWidgetManager.html#ACTION_APPWIDGET_UPDATE

我不确定是否有办法在创建小部件时不触发它。但是您可以通过将 Widget Info XML 文件中的“updatePeriodMillis”字段设置为 0 或将其保留为 空白 来阻止它再次触发。

另一种方法是将小部件 ID 存储在某处。现在,无论何时添加新的小部件,在您的 Receiver 类中,检查 ID 是否已经存在。如果它不存在(意味着一个新的小部件),那么不要执行任何代码。此外,每当删除小部件时,请从您的记录中删除小部件 ID。由于您可能删除了一个小部件,然后添加了一个与旧小部件具有相同 ID 的新小部件。

希望有帮助!

编辑: 在 Receiver 类的 onReceive() 方法中,执行以下操作:

public void onReceive(Context context, Intent intent) 
    // TODO Auto-generated method stub

    int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
    if( appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID )
    
        super.onReceive(context, intent);
    


当第一次从小部件列表中选择小部件时,它的 appWidgetId 将等于 INVALID_APPWIDGET_ID,直到它被添加到主屏幕上。由于“super.onReceive()”在选择小部件时会调用onUpdate()方法,所以不会第一次调用onUpdate()。

【讨论】:

我真的很想知道:Update-Doc: This may be sent in response to a new instance for this AppWidget provider having been instantiated, the requested update interval having lapsed, or the system booting. VS if you have declared a configuration Activity, this method is not called when the user adds the App Widget. 我已经将 updateInterval 设置为“0”,我已经按照你的建议做了(将 ID 存储在 SharedPreferences 中)。 找到了第一次不调用 onUpdate() 的方法。检查我的编辑答案。它工作正常,我刚刚在我的应用程序中实现了相同的功能。 我的 Provider 第一次触发时没有收到AppWidgetManager.INVALID_APPWIDGET_ID。事实上,我的AppWidgetConfigActivity 将收到相同的正确 ID。 (JellyBean Galaxy Nexus) INVALID_APPWIDGET_ID 总是给出一个永远不会用作小部件的有效 ID 的值。我检查时它的值为0。这是由 getIntExtra 在创建小部件时返回​​的。之后,每当为同一个小部件调用 onReceive() 时,它都会返回该小部件的有效 ID。也许差异即将到来,因为我正在使用 Android 2.3.3 (API 10) 而你正在使用 Jelly Bean。 我也在使用 Android 2.3.3,Rafael 所说的似乎是正确的 - 我认为我从未获得过小部件 ID 0。此外,将 updatePeriodMillis 留空并不会阻止 onUpdate每次解锁屏幕时调用。【参考方案2】:

我也遇到了这个错误。

即使我将 updatePeriodMillis 设置为 0。我的 onUpdate 在我开始 Widget 活动配置之前正在运行。我认为这是 android 小部件中的一个错误。

【讨论】:

【参考方案3】:

我解决了这个问题,方法是在 onUpdate 中向 Intent 添加一个操作,然后对 onReceive 进行 if 检查以查看是否点击了小部件:

我添加了intentClick.setAction(WIDGET_CLICKED);,它只会在单击小部件时触发。

public class WidgetProvider extends AppWidgetProvider 

    private static final String TAG = WidgetProvider.class.getSimpleName();
    private static String WIDGET_CLICKED = "widget_clicked";

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) 
    Log.e(TAG, "onUpdate()");

    remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
    watchWidget = new ComponentName(context, WidgetProvider.class);

    Intent intentClick = new Intent(context, WidgetProvider.class);
    intentClick.setAction(WIDGET_CLICKED);
    intentClick.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, "" + appWidgetIds[0]);

    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetIds[0], intentClick, 0);
    remoteViews.setOnClickPendingIntent(R.id.img_btn, pendingIntent);
    remoteViews.setInt(R.id.img_btn, "setBackgroundResource", R.drawable.play);

    appWidgetManager.updateAppWidget(watchWidget, remoteViews);


@Override
public void onReceive(Context context, Intent intent) 
    super.onReceive(context, intent);

    Log.e(TAG, "onReceive()");
    Bundle extras = intent.getExtras();

    //If AND ONLY IF WIDGET_CLICKED
    if (extras != null && intent.getAction().equals(WIDGET_CLICKED)) 
        remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);

        //If isPlaying
        if (isPlaying) 
            //setBackground as PLAY
            remoteViews.setInt(R.id.img_btn, "setBackgroundResource", R.drawable.play);
            isPlaying = false;

            Intent i = new Intent(context, MediaPlayerService.class);
            i.putExtra("command", "stopSong");
            context.startService(i);

            //If NOT playing
         else 
            //setBackground as STOP
            remoteViews.setInt(R.id.img_btn, "setBackgroundResource", R.drawable.stop);
            isPlaying = true;

            Intent i = new Intent(context, MediaPlayerService.class);
            i.putExtra("command", "playRandomSong");
            context.startService(i);
        

        watchWidget = new ComponentName(context, WidgetProvider.class);

        (AppWidgetManager.getInstance(context)).updateAppWidget(watchWidget, remoteViews);
    

【讨论】:

以上是关于防止触发 WidgetProvider 的 onUpdate的主要内容,如果未能解决你的问题,请参考以下文章

WidgetProvider 的 appWidgetIds[] 是啥?为啥总是得到它的价值0?

ONU弱光对网速的影响

ONU弱光对网速的影响

ONU弱光对网速的影响

光猫手机自动激活系统-开发指南-002- 重启ONU(RESET-ONU)

光猫手机自动激活系统-开发指南-001- OLT ONU信息(LST-ONU)