Android应用程序注冊广播接收器(registerReceiver)的过程分析
?? ? ? ?前面我們介紹了Android系統的廣播機制,從本質來說,它是一種消息訂閱/公布機制,因此,使用這樣的消息驅動模型的第一步便是訂閱消息;而對Android應用程序來說,訂閱消息事實上就是注冊廣播接收器,本文將探討Android應用程序是怎樣注冊廣播接收器以及把廣播接收器注冊到哪里去的。
?? ? ? ?在Android的廣播機制中,ActivityManagerService扮演著廣播中心的角色,負責系統中全部廣播的注冊和公布操作,因此,Android應用程序注冊廣播接收器的過程就把是廣播接收器注冊到ActivityManagerService的過程。Android應用程序是通過調用ContextWrapper類的registerReceiver函數來把廣播接收器BroadcastReceiver注冊到ActivityManagerService中去的,而ContextWrapper類本身又借助ContextImpl類來注冊廣播接收器。
?? ? ? ?在Android應用程序框架中,Activity和Service類都繼承了ContextWrapper類,因此,我們能夠在Activity或者Service的子類中調用registerReceiver函數來注冊廣播接收器。Activity、Service、ContextWrapper和ContextImpl這四個類的關系能夠參考前面Android系統在新進程中啟動自己定義服務過程(startService)的原理分析一文中描寫敘述的Activity類圖。
?? ? ? ?這篇文章還是繼續以實例來進行情景分析,所用到的樣例便是上一篇文章Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃里面介紹的應用程序了,所以希望讀者在繼續閱讀本文之前,先看看這篇文章;又因為Android應用程序是把廣播接器注冊到ActivityManagerService中去的,因此,這里又會涉入到Binder進程間通信機制,所以希望讀者對Android系統的Binder進程間通信機制有所了解,具體請參考Android進程間通信(IPC)機制Binder簡要介紹和學習計劃一文。
?? ? ? ?開始進入主題了,在Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃一文所介紹的樣例中,注冊廣播接收器的操作是MainActivity發起的,我們先來看看注冊過程的序列圖:
?? ? ? ?在分析這個序列圖之前,我們先來看一下MainActivity是怎樣調用registerReceiver函數來注冊廣播接收器的:
public class MainActivity extends Activity implements OnClickListener { ......@Override public void onResume() { super.onResume(); IntentFilter counterActionFilter = new IntentFilter(CounterService.BROADCAST_COUNTER_ACTION); registerReceiver(counterActionReceiver, counterActionFilter); } ......}?? ? ? ?MainActivity在onResume函數里,通過其父類ContextWrapper的registerReceiver函數注冊了一個BroadcastReceiver實例counterActionReceiver,而且通過IntentFilter實例counterActionFilter告訴ActivityManagerService,它要訂閱的廣播是CounterService.BROADCAST_COUNTER_ACTION類型的,這樣,ActivityManagerService在收到CounterService.BROADCAST_COUNTER_ACTION類型的廣播時,就會分發給counterActionReceiver實例的onReceive函數。?? ? ? ?接下來,就開始分析注冊過程中的每個步驟了。
?? ? ? ?Step 1. ContextWrapper.registerReceiver
?? ? ? ?這個函數實如今frameworks/base/core/java/android/content/ContextWrapper.java文件里:
public class ContextWrapper extends Context {Context mBase;......@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {return mBase.registerReceiver(receiver, filter);}......}?? ? ? ?這里的成員變量mBase是一個ContextImpl實例,想知道為什么,能夠回過頭去看看Android應用程序啟動過程源碼分析這篇文章>~<。?? ? ? ?Step 2. ContextImpl.registerReceiver
?? ? ? ?這個函數實如今frameworks/base/core/java/android/app/ContextImpl.java文件里:
class ContextImpl extends Context {......@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {return registerReceiver(receiver, filter, null, null);}@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler) {return registerReceiverInternal(receiver, filter, broadcastPermission,scheduler, getOuterContext());}private Intent registerReceiverInternal(BroadcastReceiver receiver,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {IIntentReceiver rd = null;if (receiver != null) {if (mPackageInfo != null && context != null) {if (scheduler == null) {scheduler = mMainThread.getHandler();}rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);} else {......}}try {return ActivityManagerNative.getDefault().registerReceiver(mMainThread.getApplicationThread(),rd, filter, broadcastPermission);} catch (RemoteException e) {return null;}}......}?? ? ? ?通過兩個函數的中轉,終于就進入到ContextImpl.registerReceiverInternal這個函數來了。這里的成員變量mPackageInfo是一個LoadedApk實例,它是用來負責處理廣播的接收的,在后面一篇文章講到廣播的發送時(sendBroadcast),會具體描寫敘述。參數broadcastPermission和scheduler都為null,而參數context是上面的函數通過調用函數getOuterContext得到的,這里它就是指向MainActivity了,因為MainActivity是繼承于Context類的,因此,這里用Context類型來引用。?? ? ? ?因為條件mPackageInfo != null和context != null都成立,而且條件scheduler == null也成立,于是就調用mMainThread.getHandler來獲得一個Handler了,這個Hanlder是后面用來分發ActivityManagerService發送過的廣播用的。這里的成員變量mMainThread是一個ActivityThread實例,在前面Android應用程序啟動過程源碼分析這篇文章也描寫敘述過了。我們先來看看ActivityThread.getHandler函數的實現,然后再回過頭來繼續分析ContextImpl.registerReceiverInternal函數。
?? ? ? ?Step 3. ActivityThread.getHandler
?? ? ? ?這個函數實如今frameworks/base/core/java/android/app/ActivityThread.java文件里:
?? ? ? ?再回到上一步的ContextImpl.registerReceiverInternal函數中,它通過mPackageInfo.getReceiverDispatcher函數獲得一個IIntentReceiver接口對象rd,這是一個Binder對象,接下來會把它傳給ActivityManagerService,ActivityManagerService在收到對應的廣播時,就是通過這個Binder對象來通知MainActivity來接收的。
?? ? ? ?我們也是先來看一下mPackageInfo.getReceiverDispatcher函數的實現,然后再回過頭來繼續分析ContextImpl.registerReceiverInternal函數。
?? ? ? ?Step 4. LoadedApk.getReceiverDispatcher
?? ? ? ?這個函數實如今frameworks/base/core/java/android/app/LoadedApk.java文件里:
final class LoadedApk {......public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {LoadedApk.ReceiverDispatcher rd = null;HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;if (registered) {map = mReceivers.get(context);if (map != null) {rd = map.get(r);}}if (rd == null) {rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);if (registered) {if (map == null) {map = new HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();mReceivers.put(context, map);}map.put(r, rd);}} else {rd.validate(context, handler);}return rd.getIIntentReceiver();}}......static final class ReceiverDispatcher {final static class InnerReceiver extends IIntentReceiver.Stub {final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;......InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);......}......}......final IIntentReceiver.Stub mIIntentReceiver;final Handler mActivityThread;......ReceiverDispatcher(BroadcastReceiver receiver, Context context,Handler activityThread, Instrumentation instrumentation,boolean registered) {......mIIntentReceiver = new InnerReceiver(this, !registered);mActivityThread = activityThread;......}......IIntentReceiver getIIntentReceiver() {return mIIntentReceiver;}}......}?? ? ? ?在LoadedApk.getReceiverDispatcher函數中,首先看一下參數r是不是已經有對應的ReceiverDispatcher存在了,如果有,就直接返回了,否則就新建一個ReceiverDispatcher,而且以r為Key值保在一個HashMap中,而這個HashMap以Context,這里即為MainActivity為Key值保存在LoadedApk的成員變量mReceivers中,這樣,僅僅要給定一個Activity和BroadcastReceiver,就能夠查看LoadedApk里面是否已經存在對應的廣播接收公布器ReceiverDispatcher了。
?? ? ? ?在新建廣播接收公布器ReceiverDispatcher時,會在構造函數里面創建一個InnerReceiver實例,這是一個Binder對象,實現了IIntentReceiver接口,能夠通過ReceiverDispatcher.getIIntentReceiver函數來獲得,獲得后就會把它傳給ActivityManagerService,以便接收廣播。在ReceiverDispatcher類的構造函數中,還會把傳進來的Handle類型的參數activityThread保存下來,以便后面在分發廣播的時候使用。
?? ? ? ?如今,再回到ContextImpl.registerReceiverInternal函數,在獲得了IIntentReceiver類型的Binder對象后,就開始要把它注冊到ActivityManagerService中去了。
?? ? ? ?Step 5. ActivityManagerProxy.registerReceiver
?? ? ? ?這個函數實如今frameworks/base/core/java/android/app/ActivityManagerNative.java文件里:
?? ? ? ? Step 6. ActivityManagerService.registerReceiver
?? ? ? ? 這個函數實如今frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件里:
public final class ActivityManagerService extends ActivityManagerNativeimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {......public Intent registerReceiver(IApplicationThread caller,IIntentReceiver receiver, IntentFilter filter, String permission) {synchronized(this) {ProcessRecord callerApp = null;if (caller != null) {callerApp = getRecordForAppLocked(caller);if (callerApp == null) {......}}List allSticky = null;// Look for any matching sticky broadcasts...Iterator actions = filter.actionsIterator();if (actions != null) {while (actions.hasNext()) {String action = (String)actions.next();allSticky = getStickiesLocked(action, filter, allSticky);}} else {......}// The first sticky in the list is returned directly back to// the client.Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;......if (receiver == null) {return sticky;}ReceiverList rl= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());if (rl == null) {rl = new ReceiverList(this, callerApp,Binder.getCallingPid(),Binder.getCallingUid(), receiver);if (rl.app != null) {rl.app.receivers.add(rl);} else {......}mRegisteredReceivers.put(receiver.asBinder(), rl);}BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);rl.add(bf);......mReceiverResolver.addFilter(bf);// Enqueue broadcasts for all existing stickies that match// this filter.if (allSticky != null) {......}return sticky;}}......}?? ? ? ? 函數首先是獲得調用registerReceiver函數的應用程序進程記錄塊: ProcessRecord callerApp = null;if (caller != null) {callerApp = getRecordForAppLocked(caller);if (callerApp == null) {......}}?? ? ? ?這里得到的便是上一篇文章Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃里面介紹的應用程序Broadcast的進程記錄塊了,MainActivity就是在里面啟動起來的。?? ? ? ?
List allSticky = null;// Look for any matching sticky broadcasts...Iterator actions = filter.actionsIterator();if (actions != null) {while (actions.hasNext()) {String action = (String)actions.next();allSticky = getStickiesLocked(action, filter, allSticky);}} else {......}// The first sticky in the list is returned directly back to// the client.Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;?? ? ? ?這里傳進來的filter僅僅有一個action,就是前面描寫敘述的CounterService.BROADCAST_COUNTER_ACTION了,這里先通過getStickiesLocked函數查找一下有沒有對應的sticky intent列表存在。什么是Sticky Intent呢?我們在最后一次調用sendStickyBroadcast函數來發送某個Action類型的廣播時,系統會把代表這個廣播的Intent保存下來,這樣,后來調用registerReceiver來注冊同樣Action類型的廣播接收器,就會得到這個最后發出的廣播。這就是為什么叫做Sticky Intent了,這個最后發出的廣播盡管被處理完了,可是仍然被粘住在ActivityManagerService中,以便下一個注冊對應Action類型的廣播接收器還能繼承處理。?? ? ? ?這里,如果我們不使用sendStickyBroadcast來發送CounterService.BROADCAST_COUNTER_ACTION類型的廣播,于是,這里得到的allSticky和sticky都為null了。
?? ? ? ?繼續往下看,這里傳進來的receiver不為null,于是,繼續往下運行:
ReceiverList rl= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());if (rl == null) {rl = new ReceiverList(this, callerApp,Binder.getCallingPid(),Binder.getCallingUid(), receiver);if (rl.app != null) {rl.app.receivers.add(rl);} else {......}mRegisteredReceivers.put(receiver.asBinder(), rl);}?? ? ? ?這里事實上就是把廣播接收器receiver保存一個ReceiverList列表中,這個列表的宿主進程是rl.app,這里就是MainActivity所在的進程了,在ActivityManagerService中,用一個進程記錄塊來表示這個應用程序進程,它里面有一個列表receivers,專門用來保存這個進程注冊的廣播接收器。接著,又把這個ReceiverList列表以receiver為Key值保存在ActivityManagerService的成員變量mRegisteredReceivers中,這些都是為了方便在收到廣播時,高速找到對應的廣播接收器的。?? ? ? ?再往下看:
BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);rl.add(bf);......mReceiverResolver.addFilter(bf);?? ? ? ?上面僅僅是把廣播接收器receiver保存起來了,可是還沒有把它和filter關聯起來,這里就創建一個BroadcastFilter來把廣播接收器列表rl和filter關聯起來,然后保存在ActivityManagerService中的成員變量mReceiverResolver中去。?? ? ? ?這樣,廣播接收器注冊的過程就介紹完了,比較簡單,可是工作又比較瑣碎,主要就是將廣播接收器receiver及其要接收的廣播類型filter保存在ActivityManagerService中,以便以后能夠接收到對應的廣播并進行處理,在下一篇文章,我們將具體分析這個過程,敬請關注。
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!
轉載于:https://www.cnblogs.com/mfrbuaa/p/4295912.html
總結
以上是生活随笔為你收集整理的Android应用程序注冊广播接收器(registerReceiver)的过程分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu默认密码,及其修改
- 下一篇: Android - 广播机制和Servi