動態注冊廣播流程
- 動態注冊廣播流程
- 廣播發送流程
動態注冊廣播流程
1.在Activity中動態注冊廣播時,呼叫registerReceiver方法,會呼叫到ContextWrapper的registerReceiver方法:

2.這個方法內部,mBase其實就是一個ContextImpl型別的物件,所以會執行ContextImpl類的registerReceiver方法


經過registerReceiver多載最侄訓呼叫到registerReceiverInternal這個方法中,在這個方法中會先將scheduler先賦值為主執行緒的handler(上面傳的scheduler為null,這個Hanlder是后面用來分發ActivityManagerService發送過的廣播用的);
接下來創建一個IIntentReceiver型別的物件:如果mPackageinfo不為空呼叫mPackageInfo.getReceiverDispatcher創建,為空呼叫LoadedApk.ReceiverDispatcher創建(mPackageInfo是LoadedApk型別的物件),其實IIntentReceiver就是LoadedApk.ReceiverDispatcher.InnerReceiver型別的物件,由于InnerReceiver是繼承IIntentReceiver.Stub,所以這個InnerReceiver型別的物件其實就是一個Binder,它后面作為app行程和system_server行程進行通信的橋梁(AMS在收到相應的廣播時,就是通過這個Binder物件來通知MainActivity來接收的,),mPackageInfo是LoadedApk型別的物件,接著先分析LoadedApk類的getReceiverDispatcher方法的具體實作:


通過LoadedApk的getReceiverDispatcher方法,可以看出,它里面呼叫了LoadedApk.ReceiverDispatcher的構造方法,從上面貼出的代碼可以看到,這個構造方法里面創建了一個InnerReceiver的物件,并將這個物件賦值給mIIntentReceiver這個變數,后面通過getIIntentReceiver方法獲取的就是這個InnerReceiver型別的物件,
小結:
1.在新建廣播接收發布器ReceiverDispatcher時,會在建構式里面創建一個InnerReceiver實體,這是一個Binder物件,實作了IIntentReceiver介面,可以通過ReceiverDispatcher.getIIntentReceiver函式來獲得,獲得后就會把它傳給ActivityManagerService,以便接收廣播,
2.在ReceiverDispatcher類的建構式中,還會把傳進來的Handle型別的引數activityThread保存下來,以便后面在分發廣播的時候使用,
LockedApk中有一個mReceivers物件,以Context為key, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>為value,每一個Context都有一個這樣的hashmap,key就是receiver,value是ReceiverDispatcher,每一個receiver都對應一個ReceiverDispatcher,
總結:就是創建了一個RD物件(ReceiverDispatcher為了之后AMS和收到廣播之后遠程呼叫和可以通過handler插入到訊息佇列中去處理),這個是在LockedApk中創建的,首先LockedApk有一個mReceivers屬性,這個屬性里面存放的是key是Context,value是這個背景關系中所有注冊了receiver的集合,key是receiver,value是RD,這個RD構造方法里面保存了hander以便收到訊息之后添加到訊息佇列中,RD里面有一個靜態類InnerReceiver繼承自這個類IIntentReceiver.Stub是個代理所以可以通過他進行AMS到Rd中的通信
3.在完成了mPackageInfo.getReceiverDispatcher()創建InnerReceiver物件后,繼續呼叫了AMS的registerReceiver方法


拆開進行分析:
(1)首先獲取注冊的行程資訊

(2)接著迭代filter的actions進行呼叫getStickLocked函式查找一下有沒有對應的sticky intent串列存在,
sticky intent串列:我們在最后一次呼叫sendStickyBroadcast函式來發送某個Action型別的廣播時,系統會把代表這個廣播的Intent保存下來,這樣,后來呼叫registerReceiver來注冊相同Action型別的廣播接收器,就會得到這個最后發出的廣播,這就是為什么叫做Sticky Intent了,這個最后發出的廣播雖然被處理完了,但是仍然被粘住在ActivityManagerService中,以便下一個注冊相應Action型別的廣播接收器還能繼承處理,

(3)接下來把廣播接收器receiver保存一個ReceiverList串列中,這個串列的宿主行程是rl.app,在ActivityManagerService中,用一個行程記錄塊來表示這個應用程式行程,它里面有一個串列receivers,專門用來保存這個行程注冊的廣播接收器,接著,又把這個ReceiverList串列以receiver為Key值保存在ActivityManagerService的成員變數mRegisteredReceivers中,這些都是為了方便在收到廣播時,快速找到對應的廣播接收器的,

(4)上面只是把廣播接收器receiver保存起來了,但是還沒有把它和filter關聯起來,這里就創建一個BroadcastFilter來把廣播接收器串列rl和filter關聯起來,然后保存在ActivityManagerService中的成員變數mReceiverResolver中去,

這個方法內部,首先獲取了最后一個粘性訊息(如果有多個action,取最后一個action的最后一次粘性訊息,在最后進行回傳這個粘性訊息,如果注冊的receiver傳入的是null只傳入了actions,那么會直接回傳這個最后的粘性訊息),接著通過AMS的本地集合變數mRegisteredReceivers通過receiver(key)獲取rl(ReceiverList就是value)獲取不到進行創建rl,rl構造方法中傳入了行程記錄塊(callerApp)和Binder呼叫者的uid和pid,還有receiver,創建完后之后把當前rl添加到rl的行程記錄塊里面的receivers中,然后將receiver為key,rl為value保存添加到mRegisteredReceivers中;
receivers保存完之后開始保存filter,創建一個BroadcastFilter(將filter和rl傳入),接著把bf添加到rl中,并把bf添加到AMS本地集合mReceiverResolver中,
這樣注冊程序就完成了,
廣播發送流程
廣播的發送者將廣播發送到ActivityManagerService,ActivityManagerService接收到這個廣播以后,就會在自己的注冊中心查看有哪些廣播接收器訂閱了該廣播,然后把這個廣播逐一發送到這些廣播接收器中,但是ActivityManagerService并不等待廣播接收器處理這些廣播就回傳了,因此,廣播的發送和處理是異步的,概括來說,廣播的發送路徑就是從發送者到ActivityManagerService,再從ActivityManagerService到接收者,這中間的兩個程序都是通過Binder行程間通信機制來完成的
1.在Activity中發送廣播,sendBroadCast方法其實是呼叫了ContextWrapper的sendBroadcast方法,ContextWrapper類中的sendBroadcast方法,其實也是呼叫了ContextImpl的sendBroadcast方法
這里的resolvedType表示這個Intent的MIME型別

- 這個方法中,通過行程間通信的的方式,呼叫了system_server行程的ActivityManagerService的broadcastIntent方法

在里面呼叫了broadcastIntentLocked方法,拆開進行分析:
3. 首先對Intent進行了處理,如果broadCastReceiver所在行程死亡那么將不會收到該廣播

-
如果廣播沒有指定特性的接受者的話那么會呼叫到mReceiverResolver.queryIntent()這個方法中,在上面注冊的時候BroadcastFilter實體保存在了ActivityManagerService的成員變數mReceiverResolver中,這個BroadcastFilter實體包含了我們所注冊的廣播接收器,這里就通過mReceiverResolver.queryIntent函式將這個BroadcastFilter實體取回來,由于注冊一個廣播型別的接收器可能有多個,所以這里把所有符合條件的的BroadcastFilter實體放在一個List中,然后回傳來,

-
這里是查看一下這個intent的Intent.FLAG_RECEIVER_REPLACE_PENDING位有沒有設定,如果設定了的話,AMS就會在當前的系統中查看有沒有相同的intent還未被處理,如果有的話,就有當前這個新的intent來替換舊的intent,

-
這個if陳述句首先創建一個廣播記錄塊BroadcastRecord,里面記錄了這個廣播是由誰發出的以及要發給誰等相關資訊,由于前面得到的replacePending變數為false,這里得到的replaced變數的值也為false,于是執行下面的if陳述句,沒有替換時,才需要將新的廣播加入到BroadcastQueue.mParallelBroadcasts佇列中,等待進一步處理;進一步處理的操作由函式scheduleBroadcastsLocked進行,處理完成之后會把NR重置

上面分析的是動態廣播(NR>0因為是在動態regist的時候會把bf添加到mReceiverResolver這個AMS本地變數中,如果是靜態的是查詢不到mReceiverResolver變數里面的bf),接下來分析靜態注冊的廣播,靜態注冊的廣播receivers不為null,
小結:
1.先獲取動態注冊的廣播和靜態注冊的廣播,并將這些廣播分別存盤到兩個不同的串列中
2.判斷是否是發送的普通廣播,如果是,并且動態注冊的廣播,則先將動態注冊的廣播發送, 走后續的廣播發送流程,接著判斷是否有靜態廣播,如果有靜態廣播,則發送靜態廣播,(從原始碼代碼邏輯可以知道,普通廣播的發送,是動態廣播優先靜態廣播發送)如果發送的是有序廣播,則會將第一步的兩個串列合并到receivers串列中,并按照優先級對廣播進行排序,具體的排序規則是,優先級高的排前面,對于相同優先級的,動態優先靜態,對于優先級相同,廣播型別相同,如果都是動態廣播型別,則先注冊的優先后注冊的,對于都是靜態廣播型別,則先掃描的由于后掃描的,
3.將廣播將第二步的廣播串列,添加到BroadcastQueue的mParallelBroadcasts或者mOrderedBroadcasts中,如果廣播按照普通方式發送,則將廣播存盤在mParallelBroadcasts串列中,如果是靜態廣播存盤在mOrderedBroadcasts串列中,如果發送方式是有序廣播,則將所有的廣播(不管是靜態注冊的還是動態注冊的)都存盤在mOrderedBroadcasts串列中,

- 動態廣播:通過呼叫BroadcastQueue的enqueueOrderedBroadcastLocked方法將發送的廣播存盤在BroadcastQueue的mParallelBroadcasts串列中,然后通過scheduleBroadcastsLocked方法發送廣播,
這里的mBroadcastsScheduled表示ActivityManagerService當前是不是正在處理其它廣播,如果是的話,這里就先不處理直接回傳了,保證所有廣播串行處理,注意這里處理廣播的方式,它是通過訊息回圈來處理,每當AMS接收到一個廣播時,它就把這個廣播放進自己的訊息佇列去就完事了,根本不管這個廣播后續是處理的,因此,這里我們可以看出廣播的發送和處理是異步的,這里的成員變數mHandler是一個在ActivityManagerService內部定義的Handler類變數,通過它的sendEmptyMessage函式把一個型別為BROADCAST_INTENT_MSG的空訊息放進AMS的訊息佇列中去,這里的空訊息是指這個訊息除了有型別資訊之外,沒有任何其它額外的資訊,因為前面已經把要處理的廣播資訊都保存在mParcelBroadcasts中了,等處理這個訊息時,從mParcelBroadcasts就可以讀回相關的廣播資訊了,因此,這里不需要把廣播資訊再放在訊息內容中,

- 接下來分析processNextBroadcast函式,這個函式有點長進行分步分析

(8.1)這里傳進來的引數fromMsg為true,于是把mBroadcastScheduled重新設為false,這樣,下一個廣播就能進入到訊息佇列中進行處理了,

(8.2)無序廣播,并呼叫deliverToRegisteredReceiverLocked方法發送無序廣播

(8.3)如果是發送的有序廣播,則判斷廣播所屬的行程是否存在,并且行程未被kill,則執行processCurBroadcastLocked處理后續的流程,如果廣播所屬的行程不存在,則新建一個行程,并重復廣播的發送程序,這些情況的后續流程,如果只是發送普通股廣播,并且廣播就所屬當前行程,那么直接執行deliverToRegisteredReceiverLocked方法

(8.4)deliverToRegisteredReceiverLocked方法中,又會呼叫到performReceiveLocked方法,performReceiveLocked方法中,會呼叫app.thread.scheduleRegisteredReceiver方法,app.thread其實是ApplicationThread型別的物件,這個在之前就分析過,代碼的執行又回到了ApplicationThread類中
前面bt中app屬性保存的就是注冊廣播的行程,receiver屬性保存的是對應的所有廣播接收器,


(8.5)這個receiver就是上面ASM注冊廣播的時候傳進來的LockedApk中的InnerReceiver

(8.6)在注冊的時候rd會在構造方法中創建InnerReceiver,并把當前rd傳入到InnerReceiver的建構式中,并用弱參考保存rd

(8.7)把訊息放在訊息佇列中,然后就回傳了,這個訊息最侄訓在傳進來的Runnable型別的引數的run成員函式中進行處理,

(8.8)分發到了接收器的onReceiver方法中

參考鏈接:
https://blog.csdn.net/luoshengyang/article/details/6730748
https://blog.csdn.net/luoshengyang/article/details/6737352
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/305744.html
標籤:其他
