<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                http://www.tuicool.com/articles/22iMZj 說說?PendingIntent?的內部機制 侯 亮 ### 1 概述 在Android中,我們常常使用PendingIntent來表達一種“留待日后處理”的意思。從這個角度來說,PendingIntent可以被理解為一種特殊的異步處理機制。不過,單就命名而言,PendingIntent其實具有一定誤導性,因為它既不繼承于Intent,也不包含Intent,它的核心可以粗略地匯總成四個字——“異步激發”。 ?很明顯,這種異步激發常常是要跨進程執行的。比如說A進程作為發起端,它可以從系統“獲取”一個PendingIntent,然后A進程可以將PendingIntent對象通過binder機制“傳遞”給B進程,再由B進程在未來某個合適時機,“回調”PendingIntent對象的send()動作,完成激發。 在Android系統中,最適合做集中性管理的組件就是AMS(Activity Manager Service)啦,所以它義不容辭地承擔起管理所有PendingIntent的職責。這樣我們就可以畫出如下示意圖: ![image001](https://box.kancloud.cn/2016-03-16_56e8da7b90c01.png "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的動作了。 坦白說,這幾個函數的命名可真不怎么樣,所以我們簡單解釋一下。上面的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](https://box.kancloud.cn/2016-03-16_56e8da7ba7d7d.png "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](https://box.kancloud.cn/2016-03-16_56e8da7bbf9f8.png "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的同學有點兒幫助。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看