Android 中broadcast 註冊過程解析
來源:
前言:
本文主要解析Android 中廣播的註冊過程,其中包括動態廣播的註冊和靜態廣播的註冊。
靜態廣播:一般是在AndroidManifest.xml 中註冊,在PMS 會進行解析(詳見 android PMS 如何解析 APK)。
動態廣播:一般通過AMS 中registerReceiver(),動態存在靈活性,注意在最後unRegisterReceiver()。
基於版本:Android O
動態廣播註冊過程:
1、context.registerReceiver()
@Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { return registerReceiver(receiver, filter, null, null); } @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) { return registerReceiver(receiver, filter, null, null, flags); } @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { return registerReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext(), 0); } @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) { return registerReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext(), flags); } @Override public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler) { return registerReceiverInternal(receiver, user.getIdentifier(), filter, broadcastPermission, scheduler, getOuterContext(), 0); }
註冊的介面比較多,存在異議是在傳入的引數。
1.1 引數
receiver:這個是broadcast 中的接受者,本文的關鍵,注意其中的成員函式onReceive和成員變數mPendingResult。
filter:廣播中的過濾器。
broadcatPermission:廣播所需要的permission,後面會進行check
scheduler:執行緒Handler 物件,預設是主執行緒的handler
context:通過getOuterContext()傳入。
1.2 registerReceiverInternal
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; if (receiver != null) { //mPackageInfo 為LoadedApk if (mPackageInfo != null && context != null) { // Handler 如果沒有定義,將主執行緒的Handler傳入 if (scheduler == null) { scheduler = mMainThread.getHandler(); } //rd 為LoadedApk 中的ReceiverDispatcher,詳見【1.3】 rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver(); } } try { //最終將receiver 註冊到AMS 中,因為分發也是從AMS 中開始 final Intent intent = ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags); if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); intent.prepareToEnterProcess(); } return intent; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
見上面程式碼中的註釋,在activity/service 中註冊廣播,此時的LoadApk 是已經建立的,廣播最終會註冊到AMS,如果進行程序間呼叫,主要是通過這裡的IIntentReceiver 進行binder 通訊。LoadApk 中的ReceiverDispatcher 相當於server 端,即當時註冊廣播的activity/service 為server。最終會將binder 的server 端傳入AMS 中方便後面AMS 的分發呼叫。後面繼續解讀IIntentReceiver。
1.3 getReceiverDispatcher()
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r, Context context, Handler handler, Instrumentation instrumentation, boolean registered) { synchronized (mReceivers) { LoadedApk.ReceiverDispatcher rd = null; ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null; if (registered) { map = mReceivers.get(context); if (map != null) { rd = map.get(r); } } //首次註冊的時候rd 為null if (rd == null) { //將之前BroadcastReceiver、handler、context 都傳入 rd = new ReceiverDispatcher(r, context, handler, instrumentation, registered); if (registered) { if (map == null) { map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>(); mReceivers.put(context, map); } //map 將receiver和dispatcher對應 map.put(r, rd); } } else { rd.validate(context, handler); } rd.mForgotten = false; return rd.getIIntentReceiver(); } }
見上面程式碼中的註釋,函式最後返回的是IIntentReceiver的Binder 的server 物件。來看下ReceiverDispatcher 構造:
ReceiverDispatcher(BroadcastReceiver receiver, Context context,
Handler activityThread, Instrumentation instrumentation,
boolean registered) {
if (activityThread == null) {
throw new NullPointerException("Handler must not be null");
}
//IIntentReceiver 的server 端
mIIntentReceiver = new InnerReceiver(this, !registered);
mReceiver = receiver;
mContext = context;
mActivityThread = activityThread;
mInstrumentation = instrumentation;
mRegistered = registered;
mLocation = new IntentReceiverLeaked(null);
mLocation.fillInStackTrace();
}
1.4 AMS.registerReceiver()
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
...
ProcessRecord callerApp = null;
...
synchronized(this) {
if (caller != null) {
//獲取ProcessRecord 物件,這個是註冊端的所有資訊所在
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
...
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
instantApp = isInstantApp(callerApp, callerPackage, callingUid);
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
//獲取filter 中的actions
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {
ArrayList<String> noAction = new ArrayList<String>(1);
noAction.add(null);
actions = noAction.iterator();
}
// Collect stickies of users
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
//while 迴圈查詢sticky intent
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);
}
}
}
}
}
//處理sticky 廣播
...
// The first sticky in the list is returned directly back to the client.
Intent sticky = allSticky != null ? allSticky.get(0) : null;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
if (receiver == null) {
return sticky;
}
synchronized (this) {
//確定之前註冊廣播的程序是否已經died
if (callerApp != null && (callerApp.thread == null
|| callerApp.thread.asBinder() != caller.asBinder())) {
// Original caller already died
return null;
}
//廣播都會在mRegisteredReceivers 的map 中
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
//新註冊的receiver 會新增到mRegisteredReceivers 中
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {
throw new IllegalArgumentException(
"Receiver requested to register for uid " + callingUid
+ " was previously registered for uid " + rl.uid
+ " callerPackage is " + callerPackage);
} else if (rl.pid != callingPid) {
throw new IllegalArgumentException(
"Receiver requested to register for pid " + callingPid
+ " was previously registered for pid " + rl.pid
+ " callerPackage is " + callerPackage);
} else if (rl.userId != userId) {
throw new IllegalArgumentException(
"Receiver requested to register for user " + userId
+ " was previously registered for user " + rl.userId
+ " callerPackage is " + callerPackage);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
//根據filter,建立新的廣播接收過濾器
mReceiverResolver.addFilter(bf);
// 如果註冊的廣播為sticky,那麼就會直接加入廣播發送佇列
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
// 作為並行廣播進行處理
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
如上面code 中的註釋,其中mRegisteredReceivers記錄著所有已註冊的廣播,以receiver IBinder為key, ReceiverList為value為HashMap。
在BroadcastQueue中有兩個廣播佇列mParallelBroadcasts,mOrderedBroadcasts,資料型別都為ArrayList:
- mParallelBroadcasts:並行廣播佇列,可以立刻執行,而無需等待另一個廣播執行完成,該佇列只允許動態已註冊的廣播,從而避免發生同時拉起大量程序來執行廣播,前臺的和後臺的廣播分別位於獨立的佇列。
- mOrderedBroadcasts:有序廣播佇列,同一時間只允許執行一個廣播,該佇列頂部的廣播便是活動廣播,其他廣播必須等待該廣播結束才能執行,也是獨立區別前臺的和後臺的廣播。
其中有幾個地方需要注意:
1、getRecordForAppLocked()
final ProcessRecord getRecordForAppLocked(
IApplicationThread thread) {
if (thread == null) {
return null;
}
int appIndex = getLRURecordIndexForAppLocked(thread);
if (appIndex >= 0) {
return mLruProcesses.get(appIndex);
}
ProcessRecord 是從mLruProcesses 中獲取
2、ReceiverList
final class ReceiverList extends ArrayList<BroadcastFilter>
implements IBinder.DeathRecipient {
final ActivityManagerService owner;
public final IIntentReceiver receiver;
public final ProcessRecord app;
public final int pid;
public final int uid;
public final int userId;
BroadcastRecord curBroadcast = null;
boolean linkedToDeath = false;
我們看到最後我們會將receiver 儲存到mRegisteredReceivers,也可以說以後AMS 中每一個ReceiverList 就會對應一個LoadedApk 中的ReceiverDispatcher。
另外,ReceiverList 是繼承ArrayList<BroadcastFilter>,其中最後會根據傳入的filter 新建BroadcastFilter 的物件,並且最終新增到ReceiverList 中,這樣就形成了一幅圖。
3、sticky intent
從code 中可以看到,註冊廣播首先會確認是否是sticky 廣播,如果是的話,最終會作為並行廣播scheduleBroadcastsLocked()。
這也是sticky 廣播跟普通廣播的區別,粘性訊息在傳送後就一直存在於系統的訊息容器裡面,等待對應的處理器去處理,如果暫時沒有處理器處理這個訊息則一直在訊息容器裡面處於等待狀態,粘性廣播的Receiver如果被銷燬,那麼下次重建時會自動接收到訊息資料。
至此,廣播的動態註冊過程就基本解讀完,下面來看下靜態註冊的廣播。
靜態廣播的註冊過程:
android PMS 如何解析 APK 詳細的說明了receiver 的解析過程。當AMS呼叫PKMS的介面來查詢“和intent匹配的元件”時,PKMS內部就會去查詢當初記錄下來的資料,並把結果返回AMS。有人認為靜態receiver是常駐記憶體的,這種說法並不準確。因為常駐記憶體的只是靜態receiver的描述性資訊,並不是receiver實體本身。
這裡以BOOT_COMPLETED 廣播為例,在AMS 啟動完成的時候會發送此廣播,在此時會通過PMS 的介面進行檢查所有註冊此廣播的應用。
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
得到的ResolveInfo 為:
public class ResolveInfo implements Parcelable {
private static final String TAG = "ResolveInfo";
public ActivityInfo activityInfo;
...
...
public IntentFilter filter;