小部件的多个实例只更新最后一个小部件

24

我有一个WidgetProvider和一个Configure Activity

当Widget被启动时,它会与配置活动一起启动,并通过自定义调用WidgetProvider来设置它

(你会注意到这是来自sdk教程示例的)

 // Push widget update to surface with newly set prefix
              AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
              AwarenessWidget.updateAppWidget(context, appWidgetManager,
                      mAppWidgetId, position);

            // Make sure we pass back the original appWidgetId
            Intent resultValue = new Intent();
            resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
            setResult(RESULT_OK, resultValue);
            finish();

我将 Widget ID 传递给函数...在小部件内部,我创建了以下 Intent:

  Intent configIntent = new Intent(context, Configure.class);
    configIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

    PendingIntent pendingIntent = PendingIntent.getActivity
    (context, 0, configIntent,
    PendingIntent.FLAG_UPDATE_CURRENT);

    views.setOnClickPendingIntent(R.id.MainImage,pendingIntent);

    views.setImageViewResource(R.id.MainImage, lv_images[version]);

    appWidgetManager.updateAppWidget(appWidgetId, views);

我总是引用小部件ID,甚至将其作为意图的附加项 但是当我在主屏幕上放置两个这样的小部件时,小部件ID总是引用最后一个放置的小部件ID

3个回答

54

我有一个类似的问题。只需要在配置活动中添加以下内容,在那里设置您的PendingIntent:

Uri data = Uri.withAppendedPath(
    Uri.parse(URI_SCHEME + "://widget/id/")
    ,String.valueOf(appWidgetId));
intent.setData(data);
变量URI_SCHEME是一个字符串,可以是任何你想要的字符串。例如:"ABCD"。这将导致每个小部件都有一个唯一的PendingIntent。

我没有在我的配置活动中设置PendingIntent,只在活动调用的小部件函数中设置。 - GregM
2
这个可以运行,但我不明白为什么。有人能否进一步解释一下这个答案? - Mike
1
@Mike 经过一番研究,我发现了为什么这个答案是正确的解释。请看下面我的备选答案。 - Code Commander
在我的情况下,我遇到了一个与集合小部件有关的问题。因此,当我在主屏幕上添加了第二个小部件时,RemoteViewFactory没有被调用。所以我为启动RemoteViewService的意图粘贴了这个:intent.data = Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)),现在运行得非常好。 - Hayk Mkrtchyan

40

以下是一份更深入的解释,说明为什么你的代码无法正常工作以及如何修复它。来自于 Android SDK 文档:

一个 PendingIntent 本身只是一个引用,指向系统维护的一个令牌,描述了用于检索它的原始数据。这意味着,即使拥有它的应用程序进程被杀死,PendingIntent 本身仍然可以从其他已经获得它的进程中使用。如果创建应用稍后重新检索相同类型的 PendingIntent(相同的操作、相同的 Intent 操作、数据、类别和组件以及相同的标志),则如果该令牌仍然有效,则会接收到表示相同令牌的 PendingIntent,并且因此可以调用 cancel() 来删除它。

由于这种行为,知道两个 Intent 在检索 PendingIntent 的目的上被视为相同非常重要。人们经常犯的一个常见错误是创建多个 PendingIntent 对象,这些对象的 Intent 只在它们的“附加内容”方面有所不同,期望每次都会得到不同的 PendingIntent。但实际上并不会发生这种情况。用于匹配的 Intent 部分是由 Intent.filterEquals 定义的相同部分。如果您使用两个与 Intent.filterEquals 相等的 Intent 对象,则将为它们获取相同的 PendingIntent。

请注意,仅指定不同的“附加内容”并不足以使 PendingIntents 被视为唯一,但使用 setData 设置唯一 URI 是可以的。这就是为什么 Snailer 的 URI 解决方案“神奇地”解决了问题。

文档还提供了一个不同(可能更简单)的解决方案。在调用 getActivity 时,不要创建自定义URI,而是设置一个唯一的 requestCode:

PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);

来源: http://developer.android.com/reference/android/app/PendingIntent.html


2
requestCode 目前未被 SDK 使用,但它确实使 Intent 被视为唯一。文档建议这是 requestCode 的有效用法:“或者提供给 getActivity 不同的请求代码整数”。 - Code Commander

13

在我的测试中,使用setData(...) 对 PendingIntent 在运行Android 4.0.4 的Verizon Thunderbolt上并没有解决问题。但在其他测试设备和模拟器上可以正常工作。

我尝试了使用requestCode,而且它在所有情况下都起作用。我只需将requestCode设置为小部件ID即可:

pendingIntent = PendingIntent.getService(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接