PendingIntent学习小结

近日为解决android闹钟标签问题,分析了android的DeskClock应用,发现在AlarmReceiver.java类中用PendingIntent封装的Intent,通过putExtra存入alarm值,当标签重新设定时在当前类中值相应发生了改变,然而当通过intent启动AlarmAlertFullScreen.java类时,通过getIntent()获取Intent中的alarm.label时,标签还是第一次设定的值,百思不得其解,后来从网上搜了关于PendingIntent的用法才发现是因为Google在构造PendingIntent时第四个参数flag值设定的有问题,下面请看分析:

       一个PendingIntent就是一个Android系统管理并持有的用于描述和获取原始数据的对象的标志(引用)。这就意味着,即便创建该PendingIntent对象的进程被杀死了,这个PendingItent对象自己在其他进程中还是可用的。如果创建该PendingIntent对象的进程随后又重新获取了一个同类型的PendingIntent(对于程序来讲,就是通过同样的方法获取的,例如都是通过getActivity、getBroadcast、getService方法来获取的,并且传递给getXXX方法的Intent对象的Action是相同的,Data也是相同的,Categories也是相同的,Components也是相同的,Flags也是相同的),如果之前获取的PendingIntent对象还有效的话,那么该进程获取到的PendingItent对象将获得同一个对象的引用,而且可以通过cancel()方法来从系统中移除它。

       如果我们只是想通过设置不同的Extra来生成不同的PendingIntent对象是行不通的,因为PendingIntent对象由系统持有,并且系统只通过刚才在上面提到的几个要素来判断PendingIntent对象是否是相同的,那么如果我们想在每次更新Widget的时候也更新PendingIntent对象的话,我们应该怎么做的,目前我能想到的就是通过设置Flag的方式来做。

     

目前在Android中有以下flag:

       

FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的PendingIntent对象,那么就将先将已有的PendingIntent取消,然后重新生成一个PendingIntent对象。

FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,系统将不会创建该PendingIntent对象而是直接返回null。

FLAG_ONE_SHOT:该PendingIntent只作用一次,如果该PendingIntent对象已经触发过一次,那么下次再获取该PendingIntent并且再触发时,系统将会返回一个SendIntentException,在使用这个标志的时候一定要注意哦。

FLAG_UPDATE_CURRENT:如果系统中已存在该PendingIntent对象,那么系统将保留该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,例如更新Intent中的Extras。这个非常有用,例如之前提到的,我们需要在每次更新之后更新Intent中的Extras数据,达到在不同时机传递给MainActivity不同的参数,实现不同的效果。

最后把PendingIntent的构造函数的第四个参数flag值设为FLAG_UPDATE_CURRENT就解决闹钟标签的更新问题了。

 

参考二

IntentPendingIntent的关系,初学的时候很迷惑,用PendingIntent的时候,还会出现奇怪的问题,比如无法传递数据,无法更新数据,所以我集众家之长,加上我个人的一些实践,总结如下,希望能给你一些帮助。

首先看官方解释:An Intent is something that is used right now; a PendingIntent is something that may create an Intent in the future. You will use a PendingIntent with Notifications, AlarmManager, etc.

Intent大家都很熟悉了,就是一个意图,这个意图表明要启动哪个Activity,Service,PendingIntent可以看作是对Intent的进一步封装,它是对Intent的描述,我们可以把这个描述交给别的程序,别的程序根据这个描述在后面的时间做你安排做的事情,下面是一个发送SMS短信的例子:

  
  
  
  
  1. String msg ="你好";
  2. String number = "135****6784";
  3. SmsManager sms = SmsManager.getDefault();
  4. PendingIntent pi = PendingIntent.getBroadcast(SmsActivity.this,0,new Intent(XXX),0);
  5. sms.sendTextMessage(number, null, msg, pi, null);
  6. Toast.makeText(SmsActivity.this,"发送成功",Toast.LENGHT_LONG).show();

方法SmsManager.sendTextMessage(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent):

  1. PendingIntent sentIntent:当短信发出时,成功的话sendIntent会把其内部的描述的intent广播出去,当然失败之后会产生错误代码,并通过 android.app.PendingIntent.OnFinished进行回调("Callback");
  2. PendingIntent deliveryIntent:是当消息已经传递给收信人后所进行的Intent广播;

如果你的BroadcastReveiver注册接收相应的消息,你就会收到相应的Intent,这时候就可以根据Intent的Action,执行相应的动作,这就是上面说的in the future的含义;

有三个静态方法可以获得PendingIntent实例:

  • public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags) 
  • public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags) 
  • public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)

flags参数有三个,我觉得英文更好理解:

FLAG_ONE_SHOT:this PendingIntent can only be used once. If set, after send() is called on it, it will be automatically canceled for you and any future attempt to send through it will fail.

FLAG_UPDATE_CURRENT: 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.
这个简单解释一下,就是当存在时,先把原来的取消,然后创建一个新的,在AlarmManager服务时,修改一个闹铃,用的比较笨的的方法,先取消,然后重新注册,其实加上这个参数就行了。

要注意的是,这个只更新extra data,不会修改其他内容,不能new一个Intent,还有就是如果你的Intent中需要传递Id或者其他数据,一定要用这个flags或者FLAG_CANCEL_CURRENT,曾经一直接收不到Id,查了半天原来是这个原因 :-(

LAG_NO_CREATE:if the described PendingIntent does not already exist, then simply return null instead of creating it.

LAG_CANCEL_CURRENT:if the described PendingIntent already exists, the current one is canceled before generating a new one.You can use this to retrieve a new PendingIntent when you are only changing the extra data in the Intent; by canceling the previous pending intent, this ensures that only entities given the new data will be able to launch it. If this assurance is not an issue, consider FLAG_UPDATE_CURRENT.

上面4个flag中最经常使用的是FLAG_UPDATE_CURRENT,因为描述的Intent有更新的时候需要用到这个flag去更新你的描述(确切的说是更新extra data),否则组件在下次事件发生或时间到达的时候extras永远是第一次Intent的extras,使用FLAG_CANCEL_CURRENT也能做到更新extras,只不过是先把前面的extras清除,另外FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT的区别在于能否新new一个Intent,FLAG_CANCEL_CURRENT能够新new一个Intent,而FLAG_UPDATE_CURRENT则不能,只能使用第一次的Intent

还有一个问题就是怎么区分PendingIntent,主要取消的时候要用到,requestCode可以区分,但系统还是根据Intent的Action去区分的,如果Intent设置了Class,classData,取消的时候Intent一定要设置要相同的,不然取消不掉就可能出现取消后,Alarm还会响的问题,PendingIntent用的地方蛮多的,像 Notifications, AlarmManager等都会用到。。。

 

参考三:

刚才一个例子中用到了PendingIntent,与之前学过的Intent有些类似,所以百度了一下结合自己的理解做个整理:

       Intent是一个意图,一个描述了想要启动一个Activity、Broadcast或是Service的意图。它主要持有的信息是它想要启动的组件(Activity、Broadcast或是Service)。

        PendingIntent可以看作是对Intent的包装。供当前App之外的其他App调用。有点“被动”或是“Callback”的意思,但不是严格意义上的“被动”或是“Callback”。总之,当前App不能用它马上启动它所包裹的Intent。而是在外部App执行这个PendingIntent时,间接地、实际地调用里面的Intent。PendingIntent主要持有的信息是它所包装的Intent和当前App的Context。正由于PendingIntent中保存有当前App的Context,使它赋予外部App一种能力,使得外部App可以如同当前App一样的执行PendingIntent里的Intent,就算在执行时当前App已经不存在了,也能通过存在PendingIntent里的Context照样执行Intent。

    
  intent英文意思是意图,pending表示即将发生或来临的事情。
       PendingIntent这个类用于处理即将发生的事情。比如在通知Notification中用于跳转页面,但不是马上跳转。
       Intent 是及时启动,intent 随所在的activity 消失而消失。
       PendingIntent 可以看作是对intent的包装,通常通过getActivity,getBroadcast ,getService来得到pendingintent的实例,当前activity并不能马上启动它所包含的intent,而是在外部执行 pendingintent时,调用intent的。正由于pendingintent中 保存有当前App的Context,使它赋予外部App一种能力,使得外部App可以如同当前App一样的执行pendingintent里的 Intent, 就算在执行时当前App已经不存在了,也能通过存在pendingintent里的Context照样执行Intent。另外还可以处理intent执行 后的操作。常和alermanger 和notificationmanager一起使用。

        Intent一般是用作Activity、Sercvice、BroadcastReceiver之间传递数据,而Pendingintent,一般用在 Notification上,可以理解为延迟执行的intent,PendingIntent是对Intent一个包装。

 

你可能感兴趣的:(PendingIntent学习小结)