防止触发 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?