說說PendingIntent的內部機制

說說PendingIntent的內部機制css

 

侯 亮html

 

1 概述

        在Android中,咱們經常使用PendingIntent來表達一種「留待往後處理」的意思。從這個角度來講,PendingIntent能夠被理解爲一種特殊的異步處理機制。不過,單就命名而言,PendingIntent其實具備必定誤導性,由於它既不繼承於Intent,也不包含Intent,它的核心能夠粗略地彙總成四個字——「異步激發」。java

        很明顯,這種異步激發經常是要跨進程執行的。好比說A進程做爲發起端,它能夠從系統「獲取」一個PendingIntent,而後A進程能夠將PendingIntent對象經過binder機制「傳遞」給B進程,再由B進程在將來某個合適時機,「回調」PendingIntent對象的send()動做,完成激發。android

        在Android系統中,最適合作集中性管理的組件就是AMS(Activity Manager Service)啦,因此它責無旁貸地承擔起管理全部PendingIntent的職責。這樣咱們就能夠畫出以下示意圖:數組

image001

        注意其中的第4步「遞送相應的intent」。這一步遞送的intent是從何而來的呢?簡單地說,當發起端獲取PendingIntent時,實際上是須要同時提供若干intent的。這些intent和PendingIntent只是配套的關係,而不是聚合的關係,它們會被緩存在AMS中。往後,一旦處理端將PendingIntent的「激發」語義傳遞到AMS,AMS就會嘗試找到與這個PendingIntent對應的若干intent,並遞送出去。緩存

        固然,以上說的只是大概狀況,實際的技術細節會更復雜一點兒。下面咱們就來談談細節。異步

 

2 PendingIntent的技術細節

2.1 發起端獲取PendingIntent

        咱們先要理解,所謂的「發起端獲取PendingIntent」到底指的是什麼。難道只是簡單new一個PendingIntent對象嗎?固然不是。此處的「獲取」動做其實還含有向AMS「註冊」intent的語義。函數

        在PendingIntent.java文件中,咱們能夠看到有以下幾個比較常見的靜態函數:學習

  • public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags)
  • public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
  • public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)
  • public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags)
  • public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags, Bundle options)

它們就是咱們經常使用的獲取PendingIntent的動做了。ui

        坦白說,這幾個函數的命名可真不怎麼樣,因此咱們簡單解釋一下。上面的getActivity()的意思實際上是,獲取一個PendingIntent對象,並且該對象往後激發時所作的事情是啓動一個新activity。也就是說,當它異步激發時,會執行相似Context.startActivity()那樣的動做。相應地,getBroadcast()和getService()所獲取的PendingIntent對象在激發時,會分別執行相似Context..sendBroadcast()和Context.startService()這樣的動做。至於最後兩個getActivities(),用得比較少,激發時能夠啓動幾個activity。

        咱們以getActivity()的代碼來講明問題:

public static PendingIntent getActivity(Context context, int requestCode,
                                        Intent intent, int flags, Bundle options) 
{
    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_ACTIVITY, packageName,
                                    null, null, requestCode, new Intent[] { intent },
                                    resolvedType != null ? new String[] { resolvedType } : null,
                                    flags, options);
        return target != null ? new PendingIntent(target) : null;
    } 
    catch (RemoteException e) 
    {}
    return null;
}

其中那句new PendingIntent(target)建立了PendingIntent對象,其重要性自不待言。然而,這個對象的內部核心實際上是由上面那個getIntentSender()函數得來的。而這個IIntentSender核心纔是咱們真正須要關心的東西。

        說穿了,此處的IIntentSender對象是個binder代理,它對應的binder實體是AMS中的PendingIntentRecord對象。PendingIntent對象構造之時,IIntentSender代理做爲參數傳進來,並記錄在PendingIntent的mTarget域。往後,當PendingIntent執行異步激發時,其內部就是靠這個mTarget域向AMS傳遞語義的。

        咱們前文說過,PendingIntent經常會經由binder機制,傳遞到另外一個進程去。而binder機制能夠保證,目標進程獲得的PendingIntent的mTarget域也是合法的IIntentSender代理,並且和發起端的IIntentSender代理對應着同一個PendingIntentRecord實體。示意圖以下:

image002

 

2.2 AMS裏的PendingIntentRecord

        那麼PendingIntentRecord裏又有什麼信息呢?它的定義截選以下:

class PendingIntentRecord extends IIntentSender.Stub 
{
    final ActivityManagerService owner;
    final Key key;  // 最關鍵的key域
    final int uid;
    final WeakReference<PendingIntentRecord> ref;
    boolean sent = false;
    boolean canceled = false;
    String stringName;
    . . . . . .
}

請注意其中那個key域。這裏的Key是個PendingIntentRecord的內嵌類,其定義截選以下:

final static class Key 
{
    final int type;
    final String packageName;
    final ActivityRecord activity;
    final String who;
    final int requestCode;
    final Intent requestIntent;        // 注意!
    final String requestResolvedType;
    final Bundle options;
    Intent[] allIntents;    // 注意!記錄了當初獲取PendingIntent時,用戶所指定的全部intent
    String[] allResolvedTypes;
    final int flags;
    final int hashCode;
    . . . . . .
    . . . . . .
}

請注意其中的allIntents[]數組域以及requestIntent域。前者記錄了當初獲取PendingIntent時,用戶所指定的全部intent(雖然通常狀況下只會指定一個intent,但相似getActivities()這樣的函數仍是能夠指定多個intent的),然後者能夠粗淺地理解爲用戶所指定的那個intent數組中的最後一個intent。如今你們應該清楚異步激發時用到的intent都存在哪裏了吧。

        Key的構造函數截選以下:

Key(int _t, String _p, ActivityRecord _a, String _w,
    int _r, Intent[] _i, String[] _it, int _f, Bundle _o) 
{
    type = _t;
    packageName = _p;
    activity = _a;
    who = _w;
    requestCode = _r;
    requestIntent = _i != null ? _i[_i.length-1] : null;    // intent數組中的最後一個
    requestResolvedType = _it != null ? _it[_it.length-1] : null;
    allIntents = _i;    // 全部intent
    allResolvedTypes = _it;
    flags = _f;
    options = _o;
    . . . . . .
}

Key不光承擔着記錄信息的做用,它還承擔「鍵值」的做用。

 

2.3 AMS中的PendingIntentRecord總表

        在AMS中,管理着系統中全部的PendingIntentRecord節點,因此須要把這些節點組織成一張表:

final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> 
                                                         mIntentSenderRecords

這張哈希映射表的鍵值類型就是剛纔所說的PendingIntentRecord.Key。

        之後每當咱們要獲取PendingIntent對象時,PendingIntent裏的mTarget是這樣獲得的:AMS會先查mIntentSenderRecords表,若是能找到符合的PendingIntentRecord節點,則返回之。若是找不到,就建立一個新的PendingIntentRecord節點。由於PendingIntentRecord是個binder實體,因此通過binder機制傳遞後,客戶進程拿到的就是個合法的binder代理。如此一來,前文的示意圖能夠進一步修改爲下圖:

image003

2.4 AMS裏的getIntentSender()函數

        如今,咱們回過頭繼續說前文的getActivity(),以及其調用的getIntentSender()。咱們先列一遍getActivity()的原型:

public static PendingIntent getActivity(Context context, int requestCode,
                                        Intent intent, int flags, Bundle options)
  • context參數是調用方的上下文。
  • requestCode是個簡單的整數,起區分做用。
  • intent是異步激發時將發出的intent。
  • flags能夠包含一些既有的標識,好比FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT、FLAG_UPDATE_CURRENT等等。很多同窗對這個域不是很清楚,咱們後文會細說。
  • options能夠攜帶一些額外的數據。

        getActivity()的代碼很簡單,其參數基本上都傳給了getIntentSender()。

IIntentSender target = ActivityManagerNative.getDefault().getIntentSender(. . . . . .)

getIntentSender()的原型大致是這樣的:

public IIntentSender getIntentSender(int type,
            String packageName, IBinder token, String resultWho,
            int requestCode, Intent[] intents, String[] resolvedTypes,
            int flags, Bundle options) throws RemoteException;

其參數比getActivity()要多一些,咱們逐個說明。

        type參數代表PendingIntent的類型。getActivity()和getActivities()動做裏指定的類型值是INTENT_SENDER_ACTIVITY,getBroadcast()和getService()和動做裏指定的類型值分別是INTENT_SENDER_BROADCAST和INTENT_SENDER_SERVICE。另外,在Activity.java文件中,咱們還看到一個createPendingResult()函數,這個函數表達了發起方的activity往後但願獲得result回饋的意思,因此其內部調用getIntentSender()時指定的類型值爲INTENT_SENDER_ACTIVITY_RESULT。

        packageName參數表示發起端所屬的包名。

        token參數是個指代回饋目標方的代理。這是什麼意思呢?咱們經常使用的getActivity()、getBroadcast()和getService()中,只是把這個參數簡單地指定爲null,表示這個PendingIntent激發時,是不須要發回什麼回饋的。不過當咱們但願獲取類型爲INTENT_SENDER_ACTIVITY_RESULT的PendingIntent時,就須要指定token參數了。具體可參考createPendingResult()的代碼:

public PendingIntent createPendingResult(int requestCode, Intent data, int flags) 
{
    String packageName = getPackageName();
    try 
    {
        data.setAllowFds(false);
        IIntentSender target = ActivityManagerNative.getDefault().getIntentSender(
                                    ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, 
                                    packageName,
                                    mParent == null ? mToken : mParent.mToken,
                                    mEmbeddedID, requestCode, new Intent[] { data }, 
                                    null, flags, null);
        return target != null ? new PendingIntent(target) : null;
    } catch (RemoteException e) {
        // Empty
    }
    return null;
}

看到了嗎?傳入的token爲Activity的mToken或者其mParent.mToken。說得簡單點兒,AMS內部能夠根據這個token找到其對應的ActivityRecord,往後當PendingIntent激發時,AMS能夠根據這個ActivityRecord肯定出該向哪一個目標進程的哪一個Activity發出result語義。

        resultWho參數和token參數息息相關,通常也是null啦。在createPendingResult()中,其值爲Activity的mEmbeddedID字符串。

        requestCode參數是個簡單的整數,能夠在獲取PendingIntent時由用戶指定,它能夠起區分的做用。

        intents數組參數是異步激發時但願發出的intent。對於getActivity()、getBroadcast()和getService()來講,都只會指定一個intent而已。只有getActivities()會嘗試一次傳入若干intent。

        resolvedTypes參數基本上和intent是相關的。通常是這樣獲得的:

String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                context.getContentResolver()) : null;

這個值經常和intent內部的mData URI有關係,好比最終的值多是URI對應的MIME類型。

        flags參數能夠指定PendingIntent的一些行爲特色。它的取值是一些既有的比特標識的組合。目前可用的標識有:FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT、FLAG_UPDATE_CURRENT等等。有時候,flags中還能夠附帶若干FILL_IN_XXX標識。咱們把常見的標識定義列舉以下:

【PendingIntent中】

public static final int FLAG_ONE_SHOT = 1<<30;
public static final int FLAG_NO_CREATE = 1<<29;
public static final int FLAG_CANCEL_CURRENT = 1<<28;
public static final int FLAG_UPDATE_CURRENT = 1<<27;

【Intent中】

public static final int FILL_IN_ACTION = 1<<0;
public static final int FILL_IN_DATA = 1<<1;
public static final int FILL_IN_CATEGORIES = 1<<2;
public static final int FILL_IN_COMPONENT = 1<<3;
public static final int FILL_IN_PACKAGE = 1<<4;
public static final int FILL_IN_SOURCE_BOUNDS = 1<<5;
public static final int FILL_IN_SELECTOR = 1<<6;
public static final int FILL_IN_CLIP_DATA = 1<<7;

這些以FILL_IN_打頭的標誌位,主要是在intent對象的fillIn()函數裏起做用:

public int fillIn(Intent other, int flags)

咱們以FILL_IN_ACTION爲例來講明,當咱們執行相似srcIntent.fillIn(otherIntent, ...)的句子時,若是otherIntent的mAction域不是null值,那麼fillIn()會在如下兩種狀況下,用otherIntent的mAction域值爲srcIntent的mAction域賦值:

1) 當srcIntent的mAction域值爲null時;
2) 若是fillIn的flags參數裏攜帶了FILL_IN_ACTION標誌位,那麼即使srcIntent的mAction已經有值了,此時也會用otherIntent的mAction域值強行替換掉srcIntent的mAction域值。

其餘FILL_IN_標誌位和FILL_IN_ACTION的處理方式相似,咱們再也不贅述。

        options參數能夠攜帶一些額外數據。

 

2.4.1 getIntentSender()函數

        getIntentSender()函數摘錄以下:

public IIntentSender getIntentSender(int type, String packageName, 
                                     IBinder token, String resultWho,
                                     int requestCode, Intent[] intents, 
                                     String[] resolvedTypes,
                                     int flags, Bundle options) 
{
    . . . . . .    
    // 先判斷intents數組,能夠用僞代碼checkIntents(intents)來表示
    // checkIntents(intents);
    . . . . . .    
    int callingUid = Binder.getCallingUid();
    . . . . . .
    if (callingUid != 0 && callingUid != Process.SYSTEM_UID) 
    {
        int uid = AppGlobals.getPackageManager().getPackageUid(packageName, 
                                                               UserId.getUserId(callingUid));
        if (!UserId.isSameApp(callingUid, uid)) 
        {
            . . . . . .
            throw new SecurityException(msg);
        }
    }
    . . . . . .
    return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
            token, resultWho, requestCode, intents, resolvedTypes, flags, options);
    . . . . . .
}

getIntentSender()函數中有一段逐條判斷intents[]的代碼,我用僞代碼checkIntents(intents)來表示,這部分對應的實際代碼以下:

for (int i=0; i<intents.length; i++) 
{
    Intent intent = intents[i];
    if (intent != null) 
    {
        if (intent.hasFileDescriptors()) 
        {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        if (type == ActivityManager.INTENT_SENDER_BROADCAST &&
            (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) 
        {
            throw new IllegalArgumentException("Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
        }
        intents[i] = new Intent(intent);
    }
}

這段代碼說明在獲取PendingIntent對象時,intent中是不能攜帶文件描述符的。並且若是這個PendingIntent是那種要發出廣播的PendingIntent,那麼intent中也不能攜帶FLAG_RECEIVER_BOOT_UPGRADE標識符。「BOOT_UPGRADE」應該是「啓動並升級」的意思,它不能使用PendingIntent。

        getIntentSender()中最核心的一句應該是調用getIntentSenderLocked()的那句。

 

2.4.2 getIntentSenderLocked()函數

        getIntentSenderLocked()的代碼截選以下:

【frameworks/base/services/java/com/android/server/am/ActivityManagerService.java】

IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, 
                                    IBinder token, String resultWho,
                                    int requestCode, Intent[] intents, 
                                    String[] resolvedTypes, int flags,
                                    Bundle options) 
{
    . . . . . .
    // 若是是INTENT_SENDER_ACTIVITY_RESULT類型,那麼要判斷token所
    // 表明的activity是否還在activity棧中
    
    . . . . . .
    // 整理flags中的信息
    
    . . . . . .
    PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, 
                                                              activity, resultWho,
                                                              requestCode, intents, 
                                                              resolvedTypes, flags, options);
    // 盡力從哈希映射表中查找key對應的PendingIntentRecord,若是找不到就建立一個新的節點。
    WeakReference<PendingIntentRecord> ref;
    ref = mIntentSenderRecords.get(key);    
    PendingIntentRecord rec = ref != null ? ref.get() : null;
    if (rec != null) 
    {
        // 找到了匹配的PendingIntent,如今考慮要不要更新它,或者取消它。
        if (!cancelCurrent) 
        {
            if (updateCurrent) 
            {
                // 若是明確指定了FLAG_UPDATE_CURRENT,那麼更新找到的節點
                if (rec.key.requestIntent != null) {
                    rec.key.requestIntent.replaceExtras(intents != null ?
                            intents[intents.length - 1] : null);
                }
                if (intents != null) {
                    intents[intents.length-1] = rec.key.requestIntent;
                    rec.key.allIntents = intents;
                    rec.key.allResolvedTypes = resolvedTypes;
                } else {
                    rec.key.allIntents = null;
                    rec.key.allResolvedTypes = null;
                }
            }
            // 凡是能找到對應的節點,並且又不取消該節點的,那麼就return這個節點
            return rec;
        }
        // 若是PendingIntent的標誌中帶有FLAG_CANCEL_CURRENT,則從哈希映射表中刪除之
        rec.canceled = true;
        mIntentSenderRecords.remove(key);
    }
    if (noCreate) 
    {
        // 若是明確表示了不建立新節點,也就是說標誌中帶有FLAG_NO_CREATE,
        // 那麼不論是不是Cancel了PendingIntent,此時一律直接返回。
        return rec;
    }
    
    // 從哈希映射表中找不到,並且又沒有寫明FLAG_NO_CREATE,此時建立一個新節點
    rec = new PendingIntentRecord(this, key, callingUid);
    mIntentSenderRecords.put(key, rec.ref);
    if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) 
    {
        // 若是intent須要返回結果,那麼修改token對應的ActivityRecord
        // 的pendingResults域。
        if (activity.pendingResults == null) 
        {
            activity.pendingResults = new HashSet<WeakReference<PendingIntentRecord>>();
        }
        activity.pendingResults.add(rec.ref);
    }
    return rec;
}

 

        上面這段代碼主要作的事情有:

1) 將傳進來的多個參數信息整理成一個PendingIntentRecord.Key對象(key);
2) 嘗試從mIntentSenderRecords總表中查找和key相符的PendingIntentRecord節點;
3) 根據flags參數所含有的意義,對獲得的PendingIntentRecord進行加工。有時候修改之,有時候刪除之。
4) 若是在總表中沒有找到對應的PendingIntentRecord節點,或者根據flags的語義刪除了剛找到的節點,那麼此時的默認行爲是建立一個新的PendingIntentRecord節點,並插入總表。除非flags中明確指定了FLAG_NO_CREATE,此時不會建立新節點。

 

2.4.3 說說flags

        從getIntentSenderLocked()的代碼中,咱們終於搞明白了flags中那些特定比特值的意義了。咱們如今總結一下。

        應該說這些flags比特值基本上都是在圍繞着mIntentSenderRecords總表說事的。其中,FLAG_CANCEL_CURRENT的意思是,當咱們獲取PendingIntent時,若是能夠從總表中查到一個相符的已存在的PendingIntentRecord節點的話,那麼須要把這個節點從總表中清理出去。而在沒有指定FLAG_CANCEL_CURRENT的大前提下,若是用戶指定了FLAG_UPDATE_CURRENT標識,那麼會用新的intents參數替掉剛查到的PendingIntentRecord中的舊intents。

        而不論是剛清理了已存在的PendingIntentRecord,仍是壓根兒就沒有找到符合的PendingIntentRecord,只要用戶沒有明確指定FLAG_NO_CREATE標識,系統就會盡力建立一個新的PendingIntentRecord節點,並插入總表。

        至於FLAG_ONE_SHOT標識嘛,它並無在getIntentSenderLocked()中露臉兒。它的名字是「FLAG_ONE_SHOT」,也就是「只打一槍」的意思,那麼很明顯,這個標識起做用的地方應該是在「激發」函數裏。在最終的激發函數(sendInner())裏,咱們能夠看到下面的代碼:

【frameworks/base/services/java/com/android/server/am/PendingIntentRecord.java】

int sendInner(int code, Intent intent, String resolvedType,
        IIntentReceiver finishedReceiver, String requiredPermission,
        IBinder resultTo, String resultWho, int requestCode,
        int flagsMask, int flagsValues, Bundle options) 
{
    synchronized(owner) {
        if (!canceled) 
        {
            sent = true;
            if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) {
                owner.cancelIntentSenderLocked(this, true);
                canceled = true;
            }
            . . . . . .
            . . . . . .
        }
    }
    return ActivityManager.START_CANCELED;
}

意思很簡單,一進行激發就把相應的PendingIntentRecord節點從總表中清理出去,並且把PendingIntentRecord的canceled域設爲true。這樣,之後即使外界再調用send()動做都沒用了,由於再也沒法進入if (!canceled)判斷了。

 

2.4.4 將PendingIntentRecord節點插入總表

        接下來getIntentSenderLocked()函數new了一個PendingIntentRecord節點,並將之插入mIntentSenderRecords總表中。

 

2.5 PendingIntent的激發動做

        下面咱們來看PendingIntent的激發動做。在前文咱們已經說過,當須要激發PendingIntent之時,主要是經過調用PendingIntent的send()函數來完成激發動做的。PendingIntent提供了多個形式的send()函數,然而這些函數的內部其實調用的是同一個send(),其函數原型以下:

public void send(Context context, int code, Intent intent,
                    OnFinished onFinished, Handler handler, String requiredPermission) 
                    throws CanceledException

該函數內部最關鍵的一句是:

int res = mTarget.send(code, intent, resolvedType,
                    onFinished != null ? new FinishedDispatcher(this, onFinished, handler) : null,
                    requiredPermission);

咱們前文已經介紹過這個mTarget域了,它對應着AMS中的某個PendingIntentRecord。

        因此咱們要看一下PendingIntentRecord一側的send()函數,其代碼以下:

public int send(int code, Intent intent, String resolvedType,
                   IIntentReceiver finishedReceiver, String requiredPermission) 
{
    return sendInner(code, intent, resolvedType, finishedReceiver,
                requiredPermission, null, null, 0, 0, 0, null);
}

其中sendInner()纔是真正作激發動做的函數。

         sendInner()完成的主要邏輯動做有:

1) 若是當前PendingIntentRecord節點已經處於canceled域爲true的狀態,那麼說明這個節點已經被取消掉了,此時sendInner()不會作任何實質上的激發動做,只是簡單地return ActivityManager.START_CANCELED而已。
2) 若是當初在建立這個節點時,使用者已經指定了FLAG_ONE_SHOT標誌位的話,那麼此時sendInner()會把這個PendingIntentRecord節點從AMS中的總表中摘除,而且把canceled域設爲true。然後的操做和普通激發時的動做是一致的,也就是說也會走下面的第3)步。
3) 關於普通激發時應執行的邏輯動做是,根據當初建立PendingIntentRecord節點時,用戶指定的type類型,進行不一樣的處理。這個type其實就是咱們前文所說的INTENT_SENDER_ACTIVITY、INTENT_SENDER_BROADCAST、INTENT_SENDER_SERVICE等類型啦,你們若有興趣,可本身參考本文一開始所說的getActivity()、getBroadcast()、getService()等函數的實現代碼。

       如今還有一個問題是,既然咱們在當初獲取PendingIntent時,已經指定了往後激發時須要遞送的intent(或intent數組),那麼爲何send()動做裏還有一個intent參數呢?它們的關係又是什麼呢?我猜測,PendingIntent機制的設計者是但願給激發端一個修改「待激發的intent」的機會。好比當初咱們獲取PendingIntent對象時,若是在flags裏設置了FILL_IN_ACTION標誌位,那麼就說明咱們容許往後在某個激發點,用新的intent的mAction域值,替換掉咱們最初給的intent的mAction域值。若是一開始沒有設置FILL_IN_ACTION標誌位,並且在最初的intent裏已經有了非空的mAction域值的話,那麼即便在激發端又傳入了新intent,它也不可能修改用新intent的mAction域值替換舊intent的mAction域值。

        細心的讀者必定記得,當初獲取PendingIntent對象時,咱們但是向AMS端傳遞了一個intent數組噢,雖然通常狀況下這個數組裏只有一個intent元素,但有時候咱們也是有可能一次性傳遞多個intent的。好比getActivities()函數就能夠一次傳遞多個intent。但是如今激發動做send()卻只能傳遞一個intent參數,這該如何處理呢?答案很簡單,所傳入的intent只能影響已有的intent數組的最後一個intent元素。你們能夠看看sendInner裏allIntents[allIntents.length-1] = finalIntent;一句。

        Ok,intent說完了,下面就該作具體的激發了。咱們以簡單的INTENT_SENDER_BROADCAST型PendingIntentRecord來講明,此時的激發動做就是發送一個廣播:

owner.broadcastIntentInPackage(key.packageName, uid, finalIntent, resolvedType,
                               finishedReceiver, code, null, null,
                               requiredPermission, (finishedReceiver != null),
                               false, UserId.getUserId(uid));

至於其餘類型的PendingIntentRecord的激發動做,你們能夠自行查閱代碼,它們的基本代碼格局都是差很少的。

 

3 小結

        本文是基於我早先的一點兒筆記整理而成的。當時爲了搞清楚PendingIntent的機理,也查閱了一些網上的相關文章,只是都不大知足個人要求,後來只好本身看代碼,終於得了些本身的淺見。如今把我過去的一點兒認識整理出來,但願能對學習PendingIntent的同窗有點兒幫助。

相關文章
相關標籤/搜索