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沒有好大的做用 

相關文章
相關標籤/搜索