PendingIntent 是对真实Intent的一种封装载体,能够用来在出发时,根据Intent 唤起目标组件,如 Activity,Service,BroadcastReceiver 等。 html
例如,通常的推广行为:接收后台推送消息,并展现在通知栏上,当用户点击消息通知后,唤起指定的目标: android
Java代码
- Intent intent = new Intent(action);
-
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
对于一次性行为,上面的实现没有问题,但对于持续性的操做,问题就来了。 app
什么是持续性的操做?简单的例子就是,想豆瓣音乐客户端在通知栏上显示的那种,我称它做”远程交互“。 dom

在通栏上的交互,大体的模型是: 源码分析

做为开发者,咱们只须要关注模型中的 Notification 和 BackService 便可。当发生用户交互,通知栏上的通知视图会触发PendingIntent,并将其包含的Intent传到BackService,而后BackService根据具体的逻辑,更新对应的Notification视图,同时绑定新的PendingIntent,对应的代码以下: 性能
Java代码
- PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
为了使得新的PendingIntent生效,咱们还特意设置Flag为 PendingIntent.FLAG_UPDATE_CURRENT,ok,如今这一切都没问题。 this
那咱们稍稍把问题在搞复杂一点,我但愿PendingIntent中的Intent带上参数,像这样: spa
Java代码
- Intent intent = new Intent(action);
- intent.putExtra("data", parcelable);
而后就用PendingIntent封装,而后你再去点击具体的通知-->触发,并在代码中试图取回设置好的 data 时,你会发现取到的data有问题----点击多于二次(或者点击第 2+ 个通知)时,data的值保持不变(和第一个通知,点击第一次取得的值一致)! htm
Why? 图片
请留意:public static PendingIntent getService ( Context context, int requestCode, Intent intent, int flags)
对比 API Doc 的截图
对于参数 flags 可能的取值有: FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT
通常性而言,咱们都会选择 FLAG_UPDATE_CURRENT,直接更新当前存在的PendingIntent,以提升性能。对于FLAG_UPDATE_CURRENT 的意义解析,见下面一段Doc的原文:
写道
Flag for use with getActivity(Context, int, Intent, int), getBroadcast(Context, int, Intent, int), and getService(Context, int, Intent, int): if the described PendingIntent already exists, then keep it but its replace its extra data with what is in this new Intent. This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.
上面短文明确指出 keep it but its replace its extra data with what is in this new Intent ,这里就是全文的关键点----PendingIntent的陷阱之在!!!
对于上文中的字面意思,若是判断为新Intent,则会更新对应的extra data,可是系统是如何断定新Intent的?Object.equals?Intent.filterEquals!可是从源码分析,filrerEquals 比较拥有一样的Action,不同的data的 Intent 一定是返回false的,那问题还会出在哪呢?
很差意思,咱们还漏了一个参数:requestCode,可是doc上明写着:currently not used。类比Activity.startActivityForResult(Content content, Class<?> cls, int resquestCode)得知,resquestCode也是请求的惟一标志!
以后尝试一下的逻辑代码:
Java代码
- Intent intent = new Intent(action);
- intent.putExtra("data", parcelable);
-
- PendingIntent pendingIntent = PendingIntent.getService(context, UUID.randomUUID().hashCode(),
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
结果不言而喻......
其实从getService的源码实现能够看出一点端倪:
Java代码
- public static PendingIntent getService(Context context, int requestCode,
- Intent intent, int flags) {
- String packageName = context.getPackageName();
- String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
- context.getContentResolver()) : null;
- try {
- intent.setAllowFds(false);
- IIntentSender target =
- ActivityManagerNative.getDefault().getIntentSender(
- ActivityManager.INTENT_SENDER_SERVICE, packageName,
- null, null, requestCode, new Intent[] { intent },
- resolvedType != null ? new String[] { resolvedType } : null,
- flags, null, UserHandle.myUserId());
- return target != null ? new PendingIntent(target) : null;
- } catch (RemoteException e) {
- }
- return null;
- }
PendingIntent其实也是对 IItentSender 的一个封装,那就意味着,在更新 PendingIntent 时,系统比较的应该是 IIntentSender,从那一大串“构造参数”来看,requestCode也在其中,这关系就脱不了了。
最后,总结一句,Google 你这不是明摆着坑人吗?请看最新的最早Doc(ps:本地的SDK版本是 4.2.2):
参考资料: