1. 程式人生 > >Android應用程式註冊廣播接收器 registerReceiver 的過程分析

Android應用程式註冊廣播接收器 registerReceiver 的過程分析

               

        前面我們介紹了Android系統的廣播機制,從本質來說,它是一種訊息訂閱/釋出機制,因此,使用這種訊息驅動模型的第一步便是訂閱訊息;而對Android應用程式來說,訂閱訊息其實就是註冊廣播接收器,本文將探討Android應用程式是如何註冊廣播接收器以及把廣播接收器註冊到哪裡去的。

《Android系統原始碼情景分析》一書正在進擊的程式設計師網(http://0xcc0xcd.com)中連載,點選進入!

        在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; ...... @Override public 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 { ...... @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {  return registerReceiver(receiver, filter, null, null); } @Override public 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檔案中:

public final class ActivityThread { ...... final H mH = new H(); private final class H extends Handler {  ......  public void handleMessage(Message msg) {   ......   switch (msg.what) {   ......   }   ......  }  ...... } ...... final Handler getHandler() {  return mH; } ......}
        有了這個Handler之後,就可以分發訊息給應用程式處理了。

        再回到上一步的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檔案中:

class ActivityManagerProxy implements IActivityManager{ ...... public Intent registerReceiver(IApplicationThread caller,   IIntentReceiver receiver,   IntentFilter filter, String perm) throws RemoteException {  Parcel data = Parcel.obtain();  Parcel reply = Parcel.obtain();  data.writeInterfaceToken(IActivityManager.descriptor);  data.writeStrongBinder(caller != null ? caller.asBinder() : null);  data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);  filter.writeToParcel(data, 0);  data.writeString(perm);  mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);  reply.readException();  Intent intent = null;  int haveIntent = reply.readInt();  if (haveIntent != 0) {   intent = Intent.CREATOR.createFromParcel(reply);  }  reply.recycle();  data.recycle();  return intent; } ......}
         這個函式通過Binder驅動程式就進入到ActivityManagerService中的registerReceiver函式中去了。

         Step 6. ActivityManagerService.registerReceiver

         這個函式實現在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java檔案中:

public final class ActivityManagerService extends ActivityManagerNative  implements 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中,以便以後能夠接收到相應的廣播並進行處理,在下一篇文章,我們將詳細分析這個過程,敬請關注。