PendingIntent 是对真实Intent的一种封装载体,可以用来在出发时,根据Intent 唤起目标组件,如 Activity,Service,BroadcastReceiver 等。
例如,一般的推广行为:接收后台推送消息,并展示在通知栏上,当用户点击消息通知后,唤起指定的目标:
Intent intent = new Intent(action); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
对于一次性行为,上面的实现没有问题,但对于持续性的操作,问题就来了。
什么是持续性的操作?简单的例子就是,想豆瓣音乐客户端在通知栏上显示的那种,我称它作”远程交互“。
在通栏上的交互,大致的模型是:
作为开发者,我们只需要关注模型中的 Notification 和 BackService 即可。当发生用户交互,通知栏上的通知视图会触发PendingIntent,并将其包含的Intent传到BackService,然后BackService根据具体的逻辑,更新对应的Notification视图,同时绑定新的PendingIntent,对应的代码如下:
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
为了使得新的PendingIntent生效,我们还特地设置Flag为 PendingIntent.FLAG_UPDATE_CURRENT,ok,现在这一切都没问题。
那我们稍稍把问题在搞复杂一点,我希望PendingIntent中的Intent带上参数,像这样:
Intent intent = new Intent(action); intent.putExtra("data", parcelable);
然后就用PendingIntent封装,然后你再去点击具体的通知-->触发,并在代码中试图取回设置好的 data 时,你会发现取到的data有问题----点击多于二次(或者点击第 2+ 个通知)时,data的值保持不变(和第一个通知,点击第一次取得的值一致)!
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的原文:
上面短文明确指出 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也是请求的唯一标志!
之后尝试一下的逻辑代码:
Intent intent = new Intent(action); intent.putExtra("data", parcelable); PendingIntent pendingIntent = PendingIntent.getService(context, UUID.randomUUID().hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
结果不言而喻......
其实从getService的源码实现可以看出一点端倪:
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):
参考资料:
- http://stackoverflow.com/questions/4340431/how-can-i-correctly-pass-unique-extras-to-a-pending-intent
- http://stackoverflow.com/questions/4689029/send-extra-data-via-pendingintent-problem
- http://stackoverflow.com/questions/10537006/intent-extras-are-duplicated-when-using-flag-update-current-in-pendingintent-whe
相关推荐
NULL 博文链接:https://sunzone.iteye.com/blog/1998091
Android中pendingIntent的深入分析 pendingIntent字面意义:等待的,未决定的Intent。 要得到一个pendingIntent对象,使用方法类的静态方法 getActivity(Context, int, Intent, int),getBroadcast(Context, int, ...
这样我们就可以画出如下示意图:PendingIntent 只能通过下列的静态方法获取//获取Broadcast关联的PendingIntent PendingI
android的服务以及通知的小例子 个人存档 代码笔记见http://blog.sina.com.cn/s/blog_70356c200100y9pf.html
简单的总结了Intent和PendtingIntent的区别,经常与alermanger 和notificationmanager一起使用。
通过 PendingIntent 切换活动 这是一个小项目,用于演示通过正常调用 startActivity 在活动之间切换工作正常,但是当我在 PendingIntent 中发送带有该标志的意图然后调用mPendingIntent.send()该标志似乎是忽略:-(。...
Android中pendingIntent的深入理解
要用 android.app.PendingIntent.getBroadcast(Context context, int requestCode, Intent intent)来实现控制多个闹钟,关键点在于其中的一个参数requestCode. 举例说明如下: public void setClock(){ if(lva....
import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; /*必需引用telephony.gsm.SmsManager类才能使用sendTextMessage()*/ import android.telephony.SmsManager; ...
android 自制发送短信程序 SmsManager与PendingIntent对象
归纳了Intent入 的4种形式:Intent转换与复制、 Action/Component/Data注 、PendingIntent误用与 parseUri注 入 归纳了利用自动化的工具具发现这4类形式的方法,通过批 量的扫描,可以轻易发现这些漏洞 在每种都找到了...
Google Android SDK开发范例大全(完整版)共4个分卷 目录 第1章 了解.深入.动手做. 1.1 红透半边天的Android 1.2 本书目的及涵盖范例范围 1.3 如何阅读本书 1.4 使用本书范例 1.5 参考网站 第2章 Android初体验 2.1...
本书是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点。本书从三个方面来组织内容。第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层...
Google Android SDK开发范例大全(完整版)共4个分卷 目录 第1章 了解.深入.动手做. 1.1 红透半边天的Android 1.2 本书目的及涵盖范例范围 1.3 如何阅读本书 1.4 使用本书范例 1.5 参考网站 第2章 Android初体验 2.1...
android-闹钟 使用 AlarmManager、BroadcastReceiver 和 PendingIntent 的重复 Android 警报的简单示例
Google Android SDK开发范例大全(完整版)共4个分卷 目录 第1章 了解.深入.动手做. 1.1 红透半边天的Android 1.2 本书目的及涵盖范例范围 1.3 如何阅读本书 1.4 使用本书范例 1.5 参考网站 第2章 Android初体验 2.1...
Notification的用法和PendingIntent的使用