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

Posted

技术标签:

【中文标题】PendingIntent 对第一个通知正常工作,但对其余通知不正确【英文标题】:PendingIntent works correctly for the first notification but incorrectly for the rest 【发布时间】:2011-03-11 06:07:56 【问题描述】:
  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);

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

额外信息:函数displayNotification() 在一个名为UploadManager 的类中。 Context 从实例化的activity 传递到UploadManager。函数 displayNotification() 从一个函数中多次调用,同样在 UploadManager 中,该函数在 AsyncTask 中运行。

编辑 1:我忘了提到我将字符串响应作为 extra 传递给 Intent intent

  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);

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

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

【问题讨论】:

【参考方案1】:

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


复制自 cmets:

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

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

【讨论】:

这个标志实际上也没有工作,原因相同,我认为,我的额外工作不正常(检查我的编辑 1)。 然后在 Intent 上设置一些虚拟动作,否则会丢弃额外内容。例如 intent.setAction("foo") 优秀。那行得通。我 setAction(Long.toString(System.currentTimeMillis())) 结合使用 mbauer 建议的 FLAG_UPDATE_CURRENT。使用 FLAG_ONE_SHOT 只允许我单击一次通知(这是有道理的)。非常感谢ognian。 “然后在 Intent 上设置一些虚拟操作,否则会丢弃额外内容” - 这是否记录在某处? 此外,如果您从非Activity 上下文(例如来自Service)创建PendingIntent,您仍然需要在Intent 上设置FLAG_ACTIVITY_NEW_TASK【参考方案2】:

RemoteViewsHomeScreen 小部件上的每个Button 的几个不同Intents 上苦苦挣扎。 添加这些时工作:

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

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);

【讨论】:

+1 很酷,谢谢。知道为什么添加 intent.setAction() 使它起作用吗? setAction 有效,但如果我真的必须将我的意图操作设置为其他东西怎么办?为什么框架这么有问题? 我只是喜欢 android SDK 对开发人员来说如此直观......(:♥️顺便说一句,请阅读下面的@ObjectiveTruth 答案以解释setAction的原因 出现了奇怪的行为,如果没有 setAction 方法调用 Intent Extras 将在调试时工作,但在不调试时 Intent Extras 将始终与第一次调用中传递的初始 Extras 相同。我发现在调试时,在导航出应用程序时总是调用 onCreate,但在不调试时,没有调用 onCreate,只有 onStart。调用 setAction 方法解决了这个问题,我猜这与意图不是“不同”有关,如果只是额外的值发生了变化。 @clu 因为我已经在使用setAction,你可以做的是addCategoryPendingIntent 使用 Intent.filterEquals 检查操作、数据、类型、类和类别的相等性。 developer.android.com/reference/android/content/…【参考方案3】:

Set Action 为我解决了这个问题。以下是我对情况的理解:


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

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

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

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

添加带有一些虚拟唯一字符串的 .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);

更新


如果您使用广播,则可以提供更好的解决方案。唯一的 PendingIntents 也由唯一的请求代码定义。这是我的解决方案:

//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);

【讨论】:

很好的清洁解决方案 对我来说,具有唯一 id 和 PendingIntent.FLAG_ONE_SHOT 用于待处理的意图,以及 setAction 对意图起作用。 它没有考虑额外的变化来改变意图有点奇怪,但似乎是真的:/ Unique PendingIntents 已解决问题。【参考方案4】:

我看到了答案,但没有解释。此外,没有一个答案能解决所有可能的解决方案,所以我会尽量说明这一点。

文档:

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

问题原因:

您创建了 2 个通知,其中包含 2 个待处理的意图。每个待处理的意图都与一个意图相关联:

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

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

解决方案:

您必须使每个意图都是唯一的,这样任何待处理的意图都不会相等。你如何使意图独一无二?不是你在putExtra() 中添加的额外内容。即使额外内容不同,意图可能仍然相同。要使每个 Intent 唯一,您必须为 Intent 操作、数据、类型、类、类别或请求代码设置唯一值:(其中任何一个都可以)

行动:intent.setAction(...) 数据:intent.setData(...) 类型:intent.setType(...) 类:intent.setClass(...) 分类:intent.addCategory(...) 请求代码:PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

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

【讨论】:

这最终对我有用,为每个通知使用唯一的 ID(无论如何都需要取消)和每个操作的自定义类别(在同一通知上永远不会有多个相同类型的操作) . 是的,我也为每个意图使用了一个独特的类别,效果很好。 遇到了同样的问题。两个通知同时触发。当我单击第二个通知时,什么也没发生。设置此 setAction(Long.toString(System.currentTimeMillis())); .它的工作就像魅力。感谢@MScott 的精彩解释【参考方案5】:

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

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

【讨论】:

非常感谢您花时间发布为您解决问题的内容。我忘了提到我正在向 Intent 传递一个额外的内容。这使问题更加复杂。检查我的编辑 1。【参考方案6】:

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

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

【讨论】:

这是唯一真实且正确的答案。正在寻找它,因为我想发布同样的东西。 :-)【参考方案7】:

首先,我使用PendingIntent.FLAG_CANCEL_CURRENT 比使用PendingIntent.FLAG_UPDATE_CURRENT 运气好。

【讨论】:

我完全同意这一点。如果我们可以让它取消旧的,然后创建一个新的,就没有必要用无用的额外内容来填充意图。的确,如果什么都没改变,有时它可能没用,但现在的问题是“节省内存还是节省时间”。【参考方案8】:

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

1) 清除任何意图标记

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

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

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

3) 对于 Pendingintent ,插入下面的代码

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

希望与你合作

【讨论】:

任何人都想解释为什么这个答案被否决了。这对我有用。我不知道这是否是合法的答案,但这个解决方案是完美的解决方案。至少对我来说。 也为我工作!谢谢!【参考方案9】:
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

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

【讨论】:

【参考方案10】:
// Use pending Intent and  also use unique id for display notification....
// Get a PendingIntent containing the entire back stack
PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager mNotificationManager = (NotificationManager)  sqlitewraper.context.getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(id, builder.build());

【讨论】:

【参考方案11】:

要正确发送数据,您应该发送带有待处理意图的通知 ID,如下所示: PendingIntent pendingIntent = PendingIntent.getActivity(context, (int)System.currentTimeMillis(), intent, PendingIntent.FLAG_UPDATE_CURRENT);

【讨论】:

【参考方案12】:

我有同样的问题,并使用PendingIntent.html.FLAG_UPDATE_CURRENT 解决它。

我已经检查了源代码。在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);
        

【讨论】:

【参考方案13】:

在创建 PendingIntent 对象时,我们调用 PendingIntent.getActivity(mContext, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

所以这里使用下面的代码来生成 PendingIntent 并将这个意图发送到通知。因此,当您收到另一个具有唯一 requestCode 的通知时,只能获取该通知的数据。

Random objRandom = new Random();
final PendingIntent resultPendingIntent =
        PendingIntent.getActivity(
                mContext,
                objRandom.nextInt(100),
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT
        );

【讨论】:

【参考方案14】:

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

LayoutInflater factory = LayoutInflater.from(this);            
      final View textEntryView = factory.inflate(R.layout.appointment, null);
      AlertDialog.Builder bulider= new AlertDialog.Builder(PatientDetail.this);
      final AlertDialog alert=bulider.create();


        bulider.setTitle("Enter Date/Time");
        bulider.setView(textEntryView);
        bulider.setPositiveButton("Save", new DialogInterface.OnClickListener() 

                public void onClick(DialogInterface dialog, int which) 
                      EditText typeText=(EditText) textEntryView.findViewById(R.id.Editdate);
                      EditText input1 =(EditText) textEntryView.findViewById(R.id.Edittime);
                      getDateAndTime(typeText.getText().toString(),input1.getText().toString());
                
            );
        bulider.setNegativeButton("Cancel", new DialogInterface.OnClickListener() 

                public void onClick(DialogInterface dialog, int which) 
                    dialog.cancel();
                
            );

        bulider.show();

    

【讨论】:

这与提出的问题无关。 需要澄清它与这个问题的关系。

以上是关于PendingIntent 对第一个通知正常工作,但对其余通知不正确的主要内容,如果未能解决你的问题,请参考以下文章

来自 pendingintent 的 Android 捆绑包始终接收为 null

PendingInent 创建的 Android 任务无法再次使用 pendingIntent 启动另一个活动

带有“null”PendingIntent 的通知

Notification PendingIntent Intent extras 被另一个通知覆盖

广播接收器未接收到意图 (RemoteViews / PendingIntent)

通知的 PendingIntent 不要第二次调用我的活动