Android深刻四大組件(八)廣播的註冊、發送和接收過程

前言

咱們接着來學習Android四大組件中的BroadcastReceiver,廣播主要就是分爲註冊、接收和發送過程。建議閱讀此文前請先閱讀Android深刻理解四大組件系列的文章,知識重複的部分,本文再也不贅述。html

1.廣播的註冊過程

BroadcastReceiver的註冊分爲兩種,分別是靜態註冊和動態註冊,靜態註冊在應用安裝時由PackageManagerService來完成註冊過程,關於這一過程,我會在後續的介紹PackageManagerService文章中詳細介紹。這裏只介紹BroadcastReceiver的動態註冊。
要想動態註冊BroadcastReceiver,須要調用registerReceiver方法,它的實如今ContextWrapper中,代碼以下所示。java

frameworks/base/core/java/android/content/ContextWrapper.javaandroid

@Override public Intent registerReceiver( BroadcastReceiver receiver, IntentFilter filter) { return mBase.registerReceiver(receiver, filter); }
View Code

這裏mBase具體指向就是ContextImpl,不明白的請查看Android深刻四大組件(二)Service的啓動過程這篇文章。ContextImpl的registerReceiver方法有不少重載的方法最終會調用registerReceiverInternal方法:
frameworks/base/core/java/android/app/ContextImpl.javaapp

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context) { IIntentReceiver rd = null; if (receiver != null) { if (mPackageInfo != null && context != null) {//1
              if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true);//2
          } else { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver();//3
 } } try { final Intent intent = ActivityManagerNative.getDefault().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId);//4
          if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); intent.prepareToEnterProcess(); } return intent; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }
View Code

在註釋1處判斷若是LoadedApk類型的mPackageInfo不等於null而且context不等null就調用註釋2處的代碼經過mPackageInfo的getReceiverDispatcher方法獲取rd對象,不然就調用註釋3處的代碼來建立rd對象。註釋2和3的代碼的目的都是要獲取IIntentReceiver類型的rd對象,IIntentReceiver是一個Binder接口,用於進行跨進程的通訊,它的具體實如今
LoadedApk.ReceiverDispatcher.InnerReceiver,以下所示。async

frameworks/base/core/java/android/app/LoadedApk.javaide

static final class ReceiverDispatcher { final static class InnerReceiver extends IIntentReceiver.Stub { final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher; final LoadedApk.ReceiverDispatcher mStrongRef; InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) { mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd); mStrongRef = strong ? rd : null; } ... } ... }
View Code

回到registerReceiverInternal方法,在註釋4處調用了ActivityManagerProxy(AMP)的registerReceiver方法,最終會調用AMS的registerReceiver方法,並將rd傳就去。不明白的同窗請查看Android深刻四大組件(一)應用程序啓動過程(前篇),這裏再也不贅述。
查看AMS的registerReceiver方法,以下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javaoop

public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) { ... synchronized(this) { ... Iterator<String> actions = filter.actionsIterator();//1
 ... // Collect stickies of users
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) { String action = actions.next(); for (int id : userIds) { ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id); if (stickies != null) { ArrayList<Intent> intents = stickies.get(action); if (intents != null) { if (stickyIntents == null) { stickyIntents = new ArrayList<Intent>(); } stickyIntents.addAll(intents);//2
 } } } } } ArrayList<Intent> allSticky = null; if (stickyIntents != null) { final ContentResolver resolver = mContext.getContentResolver(); for (int i = 0, N = stickyIntents.size(); i < N; i++) { Intent intent = stickyIntents.get(i); if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { allSticky = new ArrayList<Intent>(); } allSticky.add(intent);//3
 } } } ... }
View Code

註釋1處根據傳入的IntentFilter類型的filter的獲得actions列表,根據actions列表和userIds(userIds能夠理解爲應用程序的uid)獲得全部的粘性廣播的intent,並在註釋2處傳入到stickyIntents中,在註釋3處將這些粘性廣播的intent存入到allSticky列表中,從這裏能夠看出粘性廣播是存儲在AMS中的。
接着查看AMS的registerReceiver方法的剩餘內容:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javapost

public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) { ... synchronized (this) { ... ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());//1
            if (rl == null) { rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver);//2
                if (rl.app != null) { rl.app.receivers.add(rl); } ... } ... BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId);//3
            rl.add(bf);//4
            if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadcast"); } mReceiverResolver.addFilter(bf);//5
 ... return sticky; } }
View Code

註釋1處獲取ReceiverList列表,若是爲空則在註釋2處建立,ReceiverList繼承自ArrayList,用來存儲廣播接收者。在註釋3處建立BroadcastFilter並傳入此前建立的ReceiverList,BroadcastFilter用來描述註冊的廣播接收者,並在註釋4經過add方法將自身添加到ReceiverList中。註釋5處將BroadcastFilter添加到mReceiverResolver中,這樣當AMS接收到廣播時就能夠從mReceiverResolver中找到對應的廣播接收者了。下面給出廣播的註冊過程的時序圖。學習

繪圖1_副本.png繪圖1_副本.pngui

2.廣播的發送和接收過程

ContextImpl到AMS的調用過程

廣播能夠發送多種類型,包括無序廣播(普通廣播)、有序廣播和粘性廣播,這裏以無序廣播爲例,來說解廣播的發送過程。
要發送無序廣播須要調用sendBroadcast方法,它的實現一樣在ContextWrapper中:
frameworks/base/core/java/android/content/ContextWrapper.java

@Override public void sendBroadcast(Intent intent) { mBase.sendBroadcast(intent); }
View Code

接着來看ContextImpl中的sendBroadcast方法,以下所示。
frameworks/base/core/java/android/app/ContextImpl.java

@Override public void sendBroadcast(Intent intent) { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, getUserId());//1
      } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
View Code

註釋1處又是熟悉的代碼,最終會調用AMS的broadcastIntent方法:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { enforceNotIsolatedCaller("broadcastIntent"); synchronized(this) { intent = verifyBroadcastLocked(intent);//1
 ... /** * 2 */
          int res = broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky, callingPid, callingUid, userId); Binder.restoreCallingIdentity(origId); return res; } }
View Code

咱們來查看註釋1處的verifyBroadcastLocked方法:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

final Intent verifyBroadcastLocked(Intent intent) { // Refuse possible leaked file descriptors
       if (intent != null && intent.hasFileDescriptors() == true) {//1
           throw new IllegalArgumentException("File descriptors passed in Intent"); } int flags = intent.getFlags();//2
       if (!mProcessesReady) { if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {//3
           } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {//4
               Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent + " before boot completion"); throw new IllegalStateException("Cannot broadcast before boot completed"); } } if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) { throw new IllegalArgumentException( "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); } return intent; }
View Code

verifyBroadcastLocked方法主要是驗證廣播是否合法,在註釋1處驗證intent是否不爲null而且有文件描述符。註釋2處得到intent中的flag。註釋3處若是系統正在啓動過程當中,判斷若是flag設置爲FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT(啓動檢查時只接受動態註冊的廣播接收者)則不作處理,若是不是則在註釋4處判斷若是flag沒有設置爲FLAG_RECEIVER_REGISTERED_ONLY(只接受動態註冊的廣播接收者)則會拋出異常。
咱們再回到broadcastIntent方法,在註釋2處調用了broadcastIntentLocked方法,代碼以下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { ... if ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastQueue queue = broadcastQueueForIntent(intent); /** * 1 */ BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); ... boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); if (!replaced) { queue.enqueueOrderedBroadcastLocked(r); queue.scheduleBroadcastsLocked();//2
 } } ... } return Act
View Code

這裏省略了不少代碼,前面的工做主要是將動態註冊的廣播接收者和靜態註冊的廣播接收者按照優先級高低存儲在不一樣的列表中,再將這兩個列表合併到receivers列表中,這樣receivers列表包含了全部的廣播接收者(無序廣播和有序廣播)。在註釋1處建立BroadcastRecord對象並將receivers傳進去,在註釋2處調用BroadcastQueue的scheduleBroadcastsLocked方法。
這裏先給出ContextImpl到AMS的調用過程的時序圖。

繪圖8_副本.png繪圖8_副本.png

AMS到BroadcastReceiver的調用過程

BroadcastQueue的scheduleBroadcastsLocked方法的代碼以下所示。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

public void scheduleBroadcastsLocked() { ... mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));//1
    mBroadcastsScheduled = true; }
View Code

在註釋1處向BroadcastHandler類型的mHandler對象發送了BROADCAST_INTENT_MSG類型的消息,這個消息在BroadcastHandler的handleMessage方法中進行處理,以下所示。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

private final class BroadcastHandler extends Handler { public BroadcastHandler(Looper looper) { super(looper, null, true); } @Override public void handleMessage(Message msg) { switch (msg.what) { case BROADCAST_INTENT_MSG: { if (DEBUG_BROADCAST) Slog.v( TAG_BROADCAST, "Received BROADCAST_INTENT_MSG"); processNextBroadcast(true); } break; ... } } }
View Code

在handleMessage方法中調用了processNextBroadcast方法,processNextBroadcast方法對無序廣播和有序廣播分別進行處理,旨在將廣播發送給廣播接收者,下面給出processNextBroadcast方法中對無序廣播的處理部分。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

final void processNextBroadcast(boolean fromMsg) { ... if (fromMsg) { mBroadcastsScheduled = false;//1
 } // First, deliver any non-serialized broadcasts right away.
          while (mParallelBroadcasts.size() > 0) {//2
              r = mParallelBroadcasts.remove(0);//3
 ... for (int i=0; i<N; i++) { Object target = r.receivers.get(i); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Delivering non-ordered on [" + mQueueName + "] to registered "
                          + target + ": " + r); deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);//4
 } ... } }
View Code

從前面的代碼咱們得知fromMsg的值爲true,所以註釋1處會將mBroadcastsScheduled 設置爲flase,表示對於此前發來的BROADCAST_INTENT_MSG類型的消息已經處理了。註釋2處的mParallelBroadcasts列表用來存儲無序廣播,經過while循環將mParallelBroadcasts列表中的無序廣播發送給對應的廣播接收者。在註釋3處獲取每個mParallelBroadcasts列表中存儲的BroadcastRecord類型的r對象。註釋4處將這些r對象描述的廣播發送給對應的廣播接收者,deliverToRegisteredReceiverLocked方法以下所示。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

private void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered, int index) { ... try { if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST, "Delivering to " + filter + " : " + r); if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) { ... } else { performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.userId);//1
 } if (ordered) { r.state = BroadcastRecord.CALL_DONE_RECEIVE; } } catch (RemoteException e) { ... } }
View Code

這裏省去了大部分的代碼,這些代碼是用來檢查廣播發送者和廣播接收者的權限。若是經過了權限的檢查,則會調用註釋1處的performReceiveLocked方法:
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException { // Send the intent to the receiver asynchronously using one-way binder calls.
      if (app != null) {//1
          if (app.thread != null) {//2 // If we have an app thread, do the call through that so it is // correctly ordered with other one-way calls.
              try { app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.repProcState);//3 
 } } ... } else { receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser);/ } }
View Code

註釋1和2處的代碼表示若是廣播接收者所在的應用程序進程存在而且正在運行,則執行註釋3處的代碼,表示用廣播接收者所在的應用程序進程來接收廣播,這裏app.thread指的是ApplicationThread,咱們來查看ApplicationThread的scheduleRegisteredReceiver方法,代碼以下所示。
frameworks/base/core/java/android/app/ActivityThread.java

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky, int sendingUser, int processState) throws RemoteException { updateProcessState(processState, false); receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky, sendingUser);//1
 }
View Code

註釋1處調用了IIntentReceiver類型的對象receiver的performReceive方法,這裏實現receiver的類爲LoadedApk.ReceiverDispatcher.InnerReceiver,代碼以下所示。
frameworks/base/core/java/android/app/LoadedApk.java

static final class ReceiverDispatcher { final static class InnerReceiver extends IIntentReceiver.Stub { ... InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) { mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd); mStrongRef = strong ? rd : null; } @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { final LoadedApk.ReceiverDispatcher rd; ... if (rd != null) { rd.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser);//1
                } else { ... } ... }
View Code

在註釋1處調用了ReceiverDispatcher類型的rd對象的performReceive方法:
frameworks/base/core/java/android/app/LoadedApk.java

public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { final Args args = new Args(intent, resultCode, data, extras, ordered, sticky, sendingUser);//1
 ... if (intent == null || !mActivityThread.post(args)) {//2
              if (mRegistered && ordered) { IActivityManager mgr = ActivityManagerNative.getDefault(); if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, "Finishing sync broadcast to " + mReceiver); args.sendFinished(mgr); } }
View Code

在註釋1處將廣播的intent等信息封裝爲Args對象,並在註釋2處調用mActivityThread的post方法並傳入了Args對象。這個mActivityThread是一個Handler對象,具體指向的就是H,註釋2處的代碼就是將Args對象經過H發送到主線程的消息隊列中。Args繼承自Runnable,這個消息最終會在Args的run方法執行,Args的run方法以下所示。

frameworks/base/core/java/android/app/LoadedApk.java

public void run() { ... try { ClassLoader cl = mReceiver.getClass().getClassLoader(); intent.setExtrasClassLoader(cl); intent.prepareToEnterProcess(); setExtrasClassLoader(cl); receiver.setPendingResult(this); receiver.onReceive(mContext, intent);//1
     } catch (Exception e) { ... } ... }
View Code

在註釋1處執行了廣播接收者的onReceive方法,這樣註冊的廣播接收者就收到了廣播並獲得了intent。
廣播的註冊、發送和接收過程就講到這,最後給出剩餘部分的調用時序圖。
繪圖17.png

相關文章
相關標籤/搜索