Android PendingIntent 对第一个通知正常工作,但对其余通知不正确

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3168484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 09:01:07  来源:igfitidea点击:

PendingIntent works correctly for the first notification but incorrectly for the rest

androidnotificationsandroid-pendingintent

提问by ognian

  protected void displayNotification(String response) {
    Intent intent = new Intent(context, testActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

    Notification notification = new Notification(R.drawable.icon, "Upload Started", System.currentTimeMillis());
    notification.setLatestEventInfo(context, "Upload", response, pendingIntent);

    nManager.notify((int)System.currentTimeMillis(), notification);
}

This function will be called multiple times. I would like for each notificationto launch testActivity when clicked. Unfortunately, only the first notification launches testActivity. Clicking on the rest cause the notification window to minimize.

这个函数会被多次调用。我希望每个人notification在点击时都启动 testActivity。不幸的是,只有第一个通知会启动 testActivity。单击其余部分会导致通知窗口最小化。

Extra information: Function displayNotification()is in a class called UploadManager. Contextis passed into UploadManagerfrom the activitythat instantiates. Function displayNotification()is called multiple times from a function, also in UploadManager, that is running in an AsyncTask.

额外信息:函数displayNotification()在一个名为UploadManager. 从实例化的Context传入。函数被多次调用,该函数也在 UploadManager 中,该函数在.UploadManageractivitydisplayNotification()AsyncTask

Edit 1: I forgot to mention that I am passing String response into Intent intentas an extra.

编辑 1:我忘了提到我将 String 响应Intent intent作为extra.

  protected void displayNotification(String response) {
    Intent intent = new Intent(context, testActivity.class);
    intent.putExtra("response", response);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

This makes a big difference because I need the extra "response" to reflect what String response was when the notification was created. Instead, using PendingIntent.FLAG_UPDATE_CURRENT, the extra "response" reflects what String response was on the last call to displayNotification().

这有很大的不同,因为我需要额外的“响应”来反映创建通知时的字符串响应。相反,使用PendingIntent.FLAG_UPDATE_CURRENT,额外的“响应”反映了上次调用 时的字符串响应displayNotification()

I know why this is from reading the documentation on FLAG_UPDATE_CURRENT. However, I am not sure how to work around it at the moment.

我知道为什么这是通过阅读FLAG_UPDATE_CURRENT. 但是,我目前不确定如何解决它。

回答by ognian

Don't use Intent.FLAG_ACTIVITY_NEW_TASKfor PendingIntent.getActivity, use FLAG_ONE_SHOTinstead

不要Intent.FLAG_ACTIVITY_NEW_TASK用于 PendingIntent.getActivity,而是使用FLAG_ONE_SHOT



Copied from comments:

复制自评论:

Then set some dummy action on the Intent, otherwise extras are dropped. For example

然后在 Intent 上设置一些虚拟操作,否则会丢弃额外的操作。例如

intent.setAction(Long.toString(System.currentTimeMillis()))

回答by ViliusK

Was struggling with RemoteViewsand several different Intentsfor each Buttonon HomeScreenWidget. Worked when added these:

在Widget上苦苦挣扎,每个都有RemoteViews几个不同。添加这些时工作:IntentsButtonHomeScreen

1.intent.setAction(Long.toString(System.currentTimeMillis()));

1.intent.setAction(Long.toString(System.currentTimeMillis()));

2.PendingIntent.FLAG_UPDATE_CURRENT

2.PendingIntent.FLAG_UPDATE_CURRENT

        PackageManager pm = context.getPackageManager();

        Intent intent = new Intent(context, MyOwnActivity.class);
        intent.putExtra("foo_bar_extra_key", "foo_bar_extra_value");
        intent.setAction(Long.toString(System.currentTimeMillis()));
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
                intent, PendingIntent.FLAG_UPDATE_CURRENT);
        RemoteViews views = new RemoteViews(context.getPackageName(),
                R.layout.widget_layout);
        views.setOnClickPendingIntent(my_button_r_id_received_in_parameter, pendingIntent);

回答by ObjectiveTruth

Set Action Solved this for me. Here's my understanding of the situation:

设置操作为我解决了这个问题。以下是我对这种情况的理解:



I have multiple widgets that have a PendingIntent attached to each. Whenever one got updated they all got updated. The Flags are there to describe what happens with PendingIntents that are exactly the same.

我有多个小部件,每个小部件都附加了 PendingIntent。每当有人更新时,他们都会更新。Flags 用于描述完全相同的 PendingIntents 会发生什么。

FLAG_UPDATE_CURRENT description reads much better now:

FLAG_UPDATE_CURRENT 描述现在读起来好多了:

If the same PendingIntent you are making already exists, then update all the old ones to the new PendingIntent you are making.

如果您正在制作的相同 PendingIntent 已经存在,则将所有旧的更新为您正在制作的新 PendingIntent。

The definition of exactly the same looks at the whole PendingIntent EXCEPT the extras. Thus even if you have different extras on each intent (for me I was adding the appWidgetId) then to android, they're the same.

完全相同的定义着眼于整个 PendingIntent,除了额外的。因此,即使您对每个意图都有不同的附加功能(对我来说,我添加了 appWidgetId)然后到 android,它们也是相同的。

Adding .setAction with some dummy unique string tells the OS. These are completely different and don't update anything. In the end here's my implementation which works as I wanted, where each Widget has its own configuration Intent attached:

添加带有一些虚拟唯一字符串的 .setAction 告诉操作系统。这些是完全不同的,不会更新任何东西。最后,这是我的实现,它按我的意愿工作,其中每个 Widget 都有自己的配置 Intent 附加:

Intent configureIntent = new Intent(context, ActivityPreferences.class);

configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

configureIntent.setAction("dummy_unique_action_identifyer" + appWidgetId);

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

UPDATE

更新



Even better solution in case you're working with broadcasts. Unique PendingIntents are also defined by unique request codes. Here's my solution:

如果您正在使用广播,则是更好的解决方案。唯一的 PendingIntent 也由唯一的请求代码定义。这是我的解决方案:

//Weee, magic number, just want it to be positive nextInt(int r) means between 0 and r
int dummyuniqueInt = new Random().nextInt(543254); 
PendingIntent pendingClearScreenIntent = PendingIntent.getBroadcast(context, 
    dummyuniqueInt, clearScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);

回答by steliosf

I see answers but no explanations. Also none of the answers address all possible solutions, so I'll try to make that clear.

我看到答案但没有解释。也没有一个答案能解决所有可能的解决方案,所以我会尽量说清楚。

Documentation:

文档:

If you truly need multiple distinct PendingIntent objects active at the same time (such as to use as two notifications that are both shown at the same time), then you will need to ensure there is something that is different about them to associate them with different PendingIntents. This may be any of the Intent attributes considered by Intent.filterEquals, or different request code integers supplied to getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int, Intent, int), or getService(Context, int, Intent, int).

如果您确实需要同时激活多个不同的 PendingIntent 对象(例如用作同时显示的两个通知),那么您需要确保它们有一些不同之处,以便将它们与不同的待定意图。这可能是 Intent.filterEquals 考虑的任何 Intent 属性,或提供给 getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int) 的不同请求代码整数, Intent, int) 或 getService(Context, int, Intent, int)。

Cause of the problem:

问题原因:

You create 2 notifications with 2 pending intents. Each pending intent is associated with an intent:

您创建了 2 个具有 2 个待处理意图的通知。每个待处理的意图都与一个意图相关联:

Intent intent = new Intent(context, testActivity.class);

However, these 2 intents are equal, therefore when your 2nd notification arrives it will launch the first intent.

但是,这两个意图是相等的,因此当您的第二个通知到达时,它将启动第一个意图。

Solution:

解决方案:

You have to make each intent unique, so that no pending intents will ever be equal. How do you make the intents unique? Not by the extras you put with putExtra(). Even if the extras are different, the intents might still be equal. To make each intent unique, you must set a unique value to the intent action, or data, or type, or class, or category, or request code: (any of those will work)

您必须使每个意图都是独一无二的,这样任何未决意图将永远是平等的。你如何使意图独一无二?不是通过您放入的额外内容putExtra()。即使附加功能不同,意图可能仍然相同。要使每个意图唯一,您必须为意图操作、数据、类型、类、类别或请求代码设置唯一值:(任何这些都可以工作)

  • action: intent.setAction(...)
  • data: intent.setData(...)
  • type: intent.setType(...)
  • class: intent.setClass(...)
  • category: intent.addCategory(...)
  • request code: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);
  • 行动: intent.setAction(...)
  • 数据: intent.setData(...)
  • 类型: intent.setType(...)
  • 班级: intent.setClass(...)
  • 类别: intent.addCategory(...)
  • 请求代码: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Note: Setting a unique request code might be tricky because you need an int, while System.currentTimeMillis()returns long, which means that some digits will be removed. Therefore I would recommend to either go with the categoryor the actionand setting a unique string.

注意:设置唯一的请求代码可能很棘手,因为您需要一个 int,而System.currentTimeMillis()返回 long,这意味着将删除一些数字。因此,我建议使用类别操作并设置唯一字符串。

回答by mbauer14

I had the same problem, and was able to fix it by changing the flag to:

我遇到了同样的问题,并且能够通过将标志更改为:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

回答by Tomasz

As documentation said use unique request code:

正如文档所说,使用唯一的请求代码:

If you truly need multiple distinct PendingIntent objects active at the same time (such as to use as two notifications that are both shown at the same time), then you will need to ensure there is something that is different about them to associate them with different PendingIntents. This may be any of the Intent attributes considered by Intent.filterEquals, or different request code integers supplied to getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int, Intent, int), or getService(Context, int, Intent, int).

如果您确实需要同时激活多个不同的 PendingIntent 对象(例如用作同时显示的两个通知),那么您需要确保它们有一些不同之处,以便将它们与不同的待定意图。这可能是 Intent.filterEquals 考虑的任何 Intent 属性,或提供给 getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int) 的不同请求代码整数, Intent, int) 或 getService(Context, int, Intent, int)。

回答by Jon Shemitz

Fwiw, I have had better luck with PendingIntent.FLAG_CANCEL_CURRENTthan with PendingIntent.FLAG_UPDATE_CURRENT.

Fwiw,我的运气PendingIntent.FLAG_CANCEL_CURRENTPendingIntent.FLAG_UPDATE_CURRENT.

回答by Waleed A. Elgalil

I had the same problem and i fixed it by the below steps

我遇到了同样的问题,我通过以下步骤修复了它

1) Clear any flag for intent

1) 清除任何意图标记

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

2) insert intent.setAction by the below code

2)通过下面的代码插入intent.setAction

 intent.setAction(Long.toString(System.currentTimeMillis()));

3) for Pendingintent ,insert the below code

3) 对于 Pendingintent ,插入以下代码

   PendingIntent Pintent = PendingIntent.getActivity(ctx,0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

I hope to work with you

我希望和你一起工作

回答by Dmytro Ubogyi

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

In PendingIntent is two int parameters, second one and the last one. Second one is "request code" and it must be unicue number (for example id of your notifiction), else if (as in your example it equals zero, it always will be overwritten).

在 PendingIntent 中有两个 int 参数,第二个和最后一个。第二个是“请求代码”,它必须是唯一编号(例如您通知的 id),否则如果(如您的示例中它等于零,它总是会被覆盖)。

回答by qin hao

I have the same problem, and use the PendingIntent.html.FLAG_UPDATE_CURRENTto fix it.

我有同样的问题,并使用PendingIntent.html.FLAG_UPDATE_CURRENT来修复它。

I have checked the source code. In ActivityManagerService.java, the key method is as follows. When the flag is PendingIntent.FLAG_UPDATE_CURRENT and the updateCurrent is true. Some extras will be replaced by new and we will get an replaced PendingIntent.

我已经检查了源代码。在ActivityManagerService.java 中,关键方法如下。当标志为 PendingIntent.FLAG_UPDATE_CURRENT 且 updateCurrent 为真时。一些额外的将被新的替换,我们将得到一个被替换的 PendingIntent。

    IIntentSender getIntentSenderLocked(int type, String packageName,
            int callingUid, int userId, IBinder token, String resultWho,
            int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
            Bundle bOptions) {

// ... omitted

        final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
        final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
        final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
        flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
                |PendingIntent.FLAG_UPDATE_CURRENT);

        PendingIntentRecord.Key key = new PendingIntentRecord.Key(
                type, packageName, activity, resultWho,
                requestCode, intents, resolvedTypes, flags, bOptions, userId);
        WeakReference<PendingIntentRecord> ref;
        ref = mIntentSenderRecords.get(key);
        PendingIntentRecord rec = ref != null ? ref.get() : null;
        if (rec != null) {
            if (!cancelCurrent) {
                if (updateCurrent) {
                    if (rec.key.requestIntent != null) {
                        rec.key.requestIntent.replaceExtras(intents != null ?
                                intents[intents.length - 1] : null);
                    }
                    if (intents != null) {
                        intents[intents.length-1] = rec.key.requestIntent;
                        rec.key.allIntents = intents;
                        rec.key.allResolvedTypes = resolvedTypes;
                    } else {
                        rec.key.allIntents = null;
                        rec.key.allResolvedTypes = null;
                    }
                }
                return rec;
            }
            rec.canceled = true;
            mIntentSenderRecords.remove(key);
        }