PendingIntent

PendingIntent能够看做是对Intent的一个封装,但它不是马上执行某个行为,android

而是知足某些条件或触发某些事件后才执行指定的行为。网络

 

PendingIntent的获取dom

PendingIntent获取有三种方式:经过Activity,Service,BroadcastReceiver获取.ide

1. 你能够经过 getActivity(Context context, int requestCode, Intent intent, int flags) 系列方法从系统 取得一个用于启动一个Activity的PendingIntent对象.函数

2.能够经过 getService(Context context, int requestCode, Intent intent, int flags) 方法从系统取得一个 用于启动一个Service的PendingIntent对象.源码分析

3.能够经过 getBroadcast(Context context, int requestCode, Intent intent, int flags) 方法从系统取得一个用于向BroadcastReceiver的发送广播的PendingIntent对象.性能

PendingIntent的参数说明this

拿第三种方式,广播的形式说明下spa

PendingIntent sentIntent = PendingIntent.getBroadcast(this, 0,sIntent, 0);

 

第一个参数是上下文.code

第二个参数是每次requestcode不一样,就能产生多个Pendingintent.

第三个参数是用来存储信息.

第四个参数是对不一样操做做标识.

getBroadcast(Context context, int requestCode, Intent intent, int flags)中的flags有几种状态: 


1.FLAG_CANCEL_CURRENT:若是AlarmManager管理的PendingIntent已经存在,那么将会取消当前的PendingIntent,从而建立一个新的PendingIntent.

2.FLAG_UPDATE_CURRENT:若是AlarmManager管理的PendingIntent已经存在,让新的Intent更新以前Intent对象数据,
例如更新Intent中的Extras,另外,咱们也能够在PendingIntent的原进程中调用PendingIntent的cancel ()把其从系统中移除掉

3.FLAG_NO_CREATE:若是AlarmManager管理的PendingIntent已经存在,那么将不进行任何操做,若描述的Intent不存直接返回NULL(空).

4.FLAG_ONE_SHOT:该PendingIntent只做用一次.在该PendingIntent对象经过send()方法触发事后,PendingIntent将自动调用cancel()进行销毁,那么若是你再调用send()方法的话将会失败,系统将会返回一个SendIntentException.

Intent 是及时启动,intent 随所在的activity 消失而消失。 
a. Intent是当即使用的,而PendingIntent能够等到事件发生后触发,PendingIntent能够cancel
b. Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效
c. PendingIntent自带Context,而Intent须要在某个Context内运行
d. Intent在原task中运行,PendingIntent在新的task中运行

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

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

1 Intent intent = new Intent(action);  
2 PendingIntent pendingIntent = PendingIntent.
3 getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 

 

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

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

 

做为开发者,咱们只须要关注模型中的 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,如今这一切都没问题。

 

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

Java代码  

1. Intent intent = new Intent(action);  
2. intent.putExtra("data", parcelable);  

 

而后就用PendingIntent封装,而后你再去点击具体的通知-->触发,并在代码中试图取回设置好的 data 时,你会发现取到的data有问题----点击多于二次(或者点击第 2+ 个通知)时,data的值保持不变(和第一个通知,点击第一次取得的值一致)!

Why?

通常性而言,咱们都会选择 FLAG_UPDATE_CURRENT,直接更新当前存在的PendingIntent,以提升性能。对于FLAG_UPDATE_CURRENT  的意义解析,指出  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 也是请求的惟一标志!

以后尝试一下的逻辑代码:

1 Intent intent = new Intent(action);
2 
3  intent.putExtra("data", parcelable);  PendingIntent pendingIntent = 
4 
5 PendingIntent.getService(context, UUID.randomUUID().hashCode(),
6 
7                 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.                 null, null, 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也在其中,这关系就脱不了了。

 

Android的状态栏通知(Notification)

若是须要查看消息,能够拖动状态栏到屏幕下方便可查看消息。

步骤:

1获取通知管理器NotificationManager,它也是一个系统服务

2创建通知Notification notification = new Notification(icon, null, when);

3为新通知设置参数(好比声音,震动,灯光闪烁)

4把新通知添加到通知管理器

发送消息的代码以下:

//获取通知管理器

 1 NotificationManager mNotificationManager = 
 2 
 3 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)
 4 
 5 int icon = android.R.drawable.stat_notify_chat;
 6 
 7 long when = System.currentTimeMillis();//通知发生的时间为系统当前时间
 8 
 9 //新建一个通知,指定其图标和标题
10 
11 Notification notification = new Notification(icon, null, when);//第一个参数为图标,第二个参数为短暂提示标题,第三个为通知时间
12 
13 notification.defaults = Notification.DEFAULT_SOUND;//发出默认声音
14 
15 notification.flags |= Notification.FLAG_AUTO_CANCEL;//点击通知后自动清除通知
16 
17 Intent openintent = new Intent(this, OtherActivity.class);
18 
19 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, openintent, 0);//当点击消息时就会向系统发送openintent意图
20 
21 notification.setLatestEventInfo(this, “标题”, “我是内容", contentIntent);
22 
23 mNotificationManager.notify(0, notification);//第一个参数为自定义的通知惟一标识

 

重点是setLatestEventInfo( )方法的最后一个参数!!!!它是一个PendingIntent!!!!!!!!!

这里使用到了PendingIntent(pend本意是待定,不肯定的意思)

PendingIntent能够看做是对Intent的包装。PendingIntent主要持有的信息是它所包装的Intent和当前Application的Context。正因为PendingIntent中保存有当前Application的Context,使它赋予带他程序一种执行的Intent的能力,就算在执行时当前Application已经不存在了,也能经过存在PendingIntent里的Context照样执行Intent。

PendingIntent的一个很好的例子:

SmsManager的用于发送短信的方法:

sendTextMessage(destinationAddress, scAddress, text, sentIntent, deliveryIntent);

 

第一个参数:destinationAddress对方手机号码

第二个参数:scAddress短信中心号码通常设置为空

第三个参数:text短信内容

第四个参数:sentIntent判断短信是否发送成功,若是你没有SIM卡,或者网络中断,则能够经过这个itent来判断。注意强调的是“发送”的动做是否成功。那么至于对于对方是否收到,另当别论

第五个参数:deliveryIntent当短信发送到收件人时,会收到这个deliveryIntent。即强调了“发送”后的结果

就是说是在"短信发送成功"和"对方收到此短信"才会激活 sentIntent和deliveryIntent这两个Intent。这也至关因而延迟执行了Intent


上面两个例子能够理解,PendingIntent就是一个能够在知足必定条件下执行的Intent,它相比于Intent的优点在于本身携带有Context对象,这样他就没必要依赖于某个activity才能够存在。

短信系统举例代码

private final static String SEND_ACTION      = "send";private final static String DELIVERED_ACTION = "delivered";

private void sendSms(String receiver, String text) {

    SmsManager s = SmsManager.getDefault();

    PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, new Intent(SEND_ACTION),

                                                      PendingIntent.FLAG_CANCEL_CURRENT);

    PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0, new Intent(DELIVERED_ACTION),

                                                           PendingIntent.FLAG_CANCEL_CURRENT);

    // 发送完成

    registerReceiver(new BroadcastReceiver() {

 

        @Override

        public void onReceive(Context context, Intent intent) {

            switch (getResultCode()) {

                case Activity.RESULT_OK:

                    Toast.makeText(getBaseContext(), "Send Success!", Toast.LENGTH_SHORT).show();

                    break;

                case SmsManager.RESULT_ERROR_GENERIC_FAILURE:

                    Toast.makeText(getBaseContext(), "Send Failed because generic failure cause.",

                                   Toast.LENGTH_SHORT).show();

                    break;

                case SmsManager.RESULT_ERROR_NO_SERVICE:

                    Toast.makeText(getBaseContext(), "Send Failed because service is currently unavailable.",

                                   Toast.LENGTH_SHORT).show();

                    break;

                case SmsManager.RESULT_ERROR_NULL_PDU:

                    Toast.makeText(getBaseContext(), "Send Failed because no pdu provided.", Toast.LENGTH_SHORT).show();

                    break;

                case SmsManager.RESULT_ERROR_RADIO_OFF:

                    Toast.makeText(getBaseContext(), "Send Failed because radio was explicitly turned off.",

                                   Toast.LENGTH_SHORT).show();

                    break;

                default:

                    Toast.makeText(getBaseContext(), "Send Failed.", Toast.LENGTH_SHORT).show();

                    break;

            }

        }

    }, new IntentFilter(SEND_ACTION));

 

    // 对方接受完成

    registerReceiver(new BroadcastReceiver() {

 

        @Override

        public void onReceive(Context context, Intent intent) {

            switch (getResultCode()) {

                case Activity.RESULT_OK:

                    Toast.makeText(getBaseContext(), "Delivered Success!", Toast.LENGTH_SHORT).show();

                    break;

                default:

                    Toast.makeText(getBaseContext(), "Delivered Failed!", Toast.LENGTH_SHORT).show();

                    break;

            }

        }

    }, new IntentFilter(DELIVERED_ACTION));

 

    // 发送短信,sentPI和deliveredPI将分别在短信发送成功和对方接受成功时被广播

s.sendTextMessage(receiver, null, text, sentPI, delivere

I);

}

以上的两个PendingIntent sentPI和deliveredPI将分别在短信发送成功和对方接受成功时被广播

API以及一些重要方法

setResultExtras (Bundle extras)

这个函数是用来改变当前广播传来的Extra额外信息的;它只能经过Context.sendOrderedBroadcast.发送过来的广播有效;它使用Bundle来传递任意的数据,而这些数据只有接收器(broadcaster)才能解析。固然也能够把它设置为NULL,这样,它就把传来的数据映射所有清空了。

参数:

extras:新的数据映射,能够为空。

getResultExtras (boolean makeMap)获得额外的数据传入的参数时,只要不为空,那么 makeMap是否为 true 和 false 都可以获得数据。得到上一级传过来的setResultExtras(bundle);  里的数据;最后从新将bundle里的数据中添加数据。

参数:makemap没有好大的做用 

相关文章
相关标签/搜索