Android开发陷阱:利用PendingIntent传递唯一的Intent

转载 2016年05月31日 20:17:19

 PendingIntent 是对真实Intent的一种封装载体,可以用来在出发时,根据Intent 唤起目标组件,如 Activity,Service,BroadcastReceiver 等。

 

 例如,一般的推广行为:接收后台推送消息,并展示在通知栏上,当用户点击消息通知后,唤起指定的目标:

Java代码  收藏代码
  1. Intent intent = new Intent(action);  
  2.   
  3. PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);  

 

对于一次性行为,上面的实现没有问题,但对于持续性的操作,问题就来了。

什么是持续性的操作?简单的例子就是,想豆瓣音乐客户端在通知栏上显示的那种,我称它作”远程交互“。


 

在通栏上的交互,大致的模型是:


 

作为开发者,我们只需要关注模型中的 Notification 和 BackService 即可。当发生用户交互,通知栏上的通知视图会触发PendingIntent,并将其包含的Intent传到BackService,然后BackService根据具体的逻辑,更新对应的Notification视图,同时绑定新的PendingIntent,对应的代码如下:

Java代码  收藏代码
  1. PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);  

为了使得新的PendingIntent生效,我们还特地设置Flag为 PendingIntent.FLAG_UPDATE_CURRENT,ok,现在这一切都没问题。

 

那我们稍稍把问题在搞复杂一点,我希望PendingIntent中的Intent带上参数,像这样:

Java代码  收藏代码
  1. Intent intent = new Intent(action);  
  2. 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的原文:

写道
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代码  收藏代码
  1. Intent intent = new Intent(action);  
  2. intent.putExtra("data", parcelable);  
  3.   
  4. PendingIntent pendingIntent = PendingIntent.getService(context, UUID.randomUUID().hashCode(),  
  5.         intent, PendingIntent.FLAG_UPDATE_CURRENT);  

 结果不言而喻......

其实从getService的源码实现可以看出一点端倪:

Java代码  收藏代码
  1. public static PendingIntent getService(Context context, int requestCode,  
  2.         Intent intent, int flags) {  
  3.     String packageName = context.getPackageName();  
  4.     String resolvedType = intent != null ? intent.resolveTypeIfNeeded(  
  5.             context.getContentResolver()) : null;  
  6.     try {  
  7.         intent.setAllowFds(false);  
  8.         IIntentSender target =  
  9.             ActivityManagerNative.getDefault().getIntentSender(  
  10.                 ActivityManager.INTENT_SENDER_SERVICE, packageName,  
  11.                 nullnull, requestCode, new Intent[] { intent },  
  12.                 resolvedType != null ? new String[] { resolvedType } : null,  
  13.                 flags, null, UserHandle.myUserId());  
  14.         return target != null ? new PendingIntent(target) : null;  
  15.     } catch (RemoteException e) {  
  16.     }  
  17.     return null;  
  18. }  
      PendingIntent其实也是对 IItentSender 的一个封装,那就意味着,在更新 PendingIntent 时,系统比较的应该是 IIntentSender,从那一大串“构造参数”来看,requestCode也在其中,这关系就脱不了了。

 

      最后,总结一句,Google 你这不是明摆着坑人吗?请看最新的最先Doc(ps:本地的SDK版本是 4.2.2):



参考资料:


相关文章推荐

Android开发陷阱:利用PendingIntent传递唯一的Intent

PendingIntent 是对真实Intent的一种封装载体,可以用来在出发时,根据Intent 唤起目标组件,如 Activity,Service,BroadcastReceiver 等。 ...

Android开发陷阱:利用PendingIntent传递唯一的Intent

From: http://zhiweiofli.iteye.com/blog/1972513  PendingIntent 是对真实Intent的一种封装载体,可以用来在出发时,根据Intent...
  • fyfcauc
  • fyfcauc
  • 2014年12月23日 13:13
  • 233

Android开发-对于PendingIntent的简单理解

PendingIntent 就是一个可以满足在一定条件下执行的Intent,它包含了Inent及Context,所以它不必依赖与某个Activity才可以存在。   用法:一般作为参数传给某个实例,...

android开发中关于PendingIntent的知识点

今天在开发GPS时,临近警告这个部分的时候,又遇到了PendingIntent这个知识点,其实遇到过很多次了,今天就好好总结一下。 pendingIntent直观意义上的解释就是:等待的,未决定的I...

android开发之Notification结合PendingIntent使用小结

一般用在电话,短信,邮件,闹钟铃声,在手机的状态栏上就会出现一个小图标,提示用户处理这个快讯,这时手从上方滑动状态栏就可以展开并处理这个快讯。发现这个功能特别好用。 使用小结: 1。获得系统服务N...
  • zcmain
  • zcmain
  • 2013年04月26日 11:38
  • 1020

Android开发系列(十三):Intent实现Activity之间的转换以及startActivityForResult传递参数

Intent切换各个Activity以及startActivityForResult()方法来传递参数
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android开发陷阱:利用PendingIntent传递唯一的Intent
举报原因:
原因补充:

(最多只允许输入30个字)