距離上一篇博客進程的加載過了好久的時間,這中間換了一份工做,加入了新的團隊,也開始了新的項目,比較忙,因此最近纔有時間將四大組件之一的廣播原理看完,最近一段時間會相繼把四大組件分析寫完,讓咱們對四大組件有更深的瞭解。原本想一篇把廣播的內容寫完,可是發現要解釋的代碼比較多,因此仍是分開來說,那麼這篇先分析廣播的註冊和註銷,下一篇再分析廣播的發送。java
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
複製代碼
而後調用registerReceiver複寫方法:緩存
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext());
}
複製代碼
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context) {
IIntentReceiver rd = null;
// 須要註冊的廣播接收器不爲null
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
// 獲取主線程的Handler,mMainThread是描述當前應用程序進程的
scheduler = mMainThread.getHandler();
}
// 將廣播接收者receiver封裝成一個實現了IIntentReceiver接口的Binder對象rd(ReceiverDispatcher)
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
// 獲取主線程的Handler
scheduler = mMainThread.getHandler();
}
// 將廣播接收者receiver封裝成一個實現了IIntentReceiver接口的Binder對象rd(ReceiverDispatcher)
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
// 調用ActivityManagerProxy的registerReceiver,最終經過mRemote.transact方法傳遞到
// ActivityManagerService中的registerReceiver方法
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
...
return intent;
} catch (RemoteException e) {
...
}
}
複製代碼
mPackageInfo是LoadedApk類型對象,這個對象是在一個應用啓動的時候建立的。微信
// 每個註冊過廣播接收者的Activity組件在LaodApk類中都有一個對應的ReceiverDispatcher對象,它負責
// 將這個被註冊的廣播接收者與註冊它的Activity組件關聯在一塊兒。這些ReceiverDispatcher對象保存在一個
// HashMap中,而且以它們所關聯的廣播接收者爲關鍵字。最後用來保存這些ReceiverDispatcher對象的HashMap
// 又以它們所關聯的Activity組件的Context接口爲關鍵字保存在LoadApk類的成員變量mReceivers中
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) {
// 查找是否存在該廣播接收者對應的ReceiverDispatcher對象
rd = map.get(r);
}
}
if (rd == null) {// 不存在
// 初始化廣播接收器調度員
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
// 緩存ReceiverDispatcher
map.put(r, rd);
}
} else {
// 驗證廣播分發者的Context和Handler是否一致。
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
複製代碼
這裏主要是將廣播接收者receiver封裝成一個實現了IIntentReceiver接口的Binder對象rd,而後將其放置到LoadedApk對象中的mReceivers中保存起來。app
再回到上面代碼中,將生成的實現了IIntentReceiver接口的Binder對象rd經過mRemote.transact方法傳遞到ActivityManagerService中的registerReceiver方法,由於四大組件的消息傳遞都是經過這種方式實現的。異步
public Intent registerReceiver(IApplicationThread caller, String packageName, IIntentReceiver receiver, IntentFilter filter, String perm, int userId) throws RemoteException {
...
// 經過內部的一個Binder代理對象mRemote向AMS發送一個類型爲REGISTER_RECEIVER_TRANSACTION的進程
// 間通訊請求
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
...
return intent;
}
複製代碼
經過上面的mRemote.transact方法傳遞到ActivityManagerService中對應的方法:async
/** * 粘性廣播(Sticky):一個粘性廣播被髮送到AMS後,就會一直保存在AMS中,直到AMS下次再接收到另一個 * 同類型的粘性廣播爲止。一個Activity組件在向AMS註冊接收某一種烈性的廣播時,若是AMS內部切好保存這個 * 這種類型的粘性廣播,那麼AMS就會將這個粘性廣播返回給Activity組件,以便它能夠知道系統上一次發出的它 * 所感興趣的廣播內容。咱們能夠經過sendStrickyBroadcast向AMS發送粘性廣播 */
public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
int callingUid;
int callingPid;
synchronized (this) {
if (caller != null) {
// 根據caller從ProcessRecord緩存列表中查詢ProcessRecord對象caller,用來描述正在請求
// AMS註冊廣播接收者的一個Activity組件所運行在的應用程序進程
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {// 沒有對應進程,不能註冊廣播
...//拋出異常
}
...
} else {
...
}
// 獲取註冊廣播的用戶的userId(UserController是多用戶功能的用戶管理,一些系統包含訪客模式,或者多用戶,每一個用戶就會有一個id)
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
// 獲取須要註冊廣播的IntentFilter中全部的action
Iterator<String> actions = filter.actionsIterator();
// 若是註冊廣播沒有Action則添加一個null
if (actions == null) {
ArrayList<String> noAction = new ArrayList<String>(1);
noAction.add(null);
actions = noAction.iterator();
}
// Activity組件在註冊一個廣播接收者時,並非直接將這個廣播接收者註冊到了AMS中,而是將與它關聯
// 的一個InnerReceiver對象註冊到了AMS中。當AMS接收到一個廣播時,它就會根據這個廣播的類型在內
// 部知道對應的InnerReceiver對象,而後再經過這個對象將這個廣播發送給對應的廣播接收者。AMS中每
// 一個廣播接收者都是使用一個BroadcastFilter對象來描述的,而每個BroadcastFilter對象又是根
// 據它所描述的廣播接收者所關聯的一個BroadcastFilter對象,以及所要接受的廣播類型來建立。因爲在
// 一個應用程序中,不一樣的Activity組件可能會使用同一個BroadcastFilter對象來註冊不一樣的廣播接收
// 者,所以AMS會使用一個ReceiverList列表來保存這些使用了相同InnerReceiver對象來註冊的廣播接
// 收者,而且以它們所使用的InnerReceiver對象爲關鍵字。
// Collect stickies of users
// 收集與註冊用戶userId相關的全部已經被廣播過的Intent,存儲在stickyIntents中
// 包含全部用戶以及註冊廣播進程對應的用戶
// 第一個UserHandle.USER_ALL表示設備上全部的用戶,
// 第二個是callingUid對應用戶的userId(當前用戶的userId)
int[] userIds = {UserHandle.USER_ALL, UserHandle.getUserId(callingUid)};
// 這裏只經過action進行過濾
while (actions.hasNext()) {
String action = actions.next();
// 遍歷與調度進程相關的用戶id
for (int id : userIds) {
// 根據userId查詢已經發送過的對應的Intent列表
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
// 若是已經發送的Intent裏包含上面要註冊的廣播的action的Intent,將其保存到stickyIntents中
if (stickies != null) {
// 根據action查詢Intent列表
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
stickyIntents.addAll(intents);
}
}
}
}
}
// 下面對經過action過濾出來粘性廣播的Intent列表,包括:action,type,scheme,data,categories
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
// Look for any matching sticky broadcasts...
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
// 查找與IntentFilter匹配的Intent
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
...
if (receiver == null) {// 若是廣播接收器爲null,則直接返回第一個Intent結束註冊
return sticky;
}
synchronized (this) {
// 首先判斷當前進程是否還活着
if (callerApp != null && (callerApp.thread == null
|| callerApp.thread.asBinder() != caller.asBinder())) {
// Original caller already died(註冊失敗)
return null;
}
// 首先從緩存中查找註冊的receiver對應的ReceiverList(ArrayList<BroadcastFilter>),
// 第一次註冊爲null,receiver對應的是一個BroadcastFilter列表,也就是說能夠經過調用
// registerReceiver來爲receiver註冊不一樣的廣播條件。
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 {
...
}
// 這裏面最關鍵的就是下面將receiver以及對應的ReceiverList列表放到mRegisteredReceivers中
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
...
// 建立BroadcastFilter對象bf,用來描述正在註冊的廣播接收者,並添加到ReceiverList隊列rl中
// 以及mReceiverResolver中
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
...
// 添加到已註冊接收器的廣播解析器中,註冊完成
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
// 上面註冊結束之後,若是篩選出了與當前註冊的IntentFilter匹配的sticky廣播的Intent列表,
// 就將全部匹配的Intent逐條發送廣播給當前的註冊者receiver,能夠看到這裏的接受者receivers
// 裏面就只有當前建立的一個BroadcastFilter,也就是當前的註冊者。
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);
// 根據Intent返回時前臺廣播隊列仍是後臺廣播隊列
BroadcastQueue queue = broadcastQueueForIntent(intent);
// 須要發送的一條廣播記錄,receivers包含了全部能接收該條廣播的接收器
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
// 將該廣播記錄加入廣播隊列中
queue.enqueueParallelBroadcastLocked(r);
// 調度廣播,發送BROADCAST_INTENT_MSG消息,觸發處理下一個廣播。可是若是目前有廣播還在
// 發送的處理過程當中,此次推進不會起做用
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
複製代碼
上面時註冊廣播的核心代碼,主要是先判斷註冊的廣播的Action是否是已經存在AMS(ActivityManagerService)中的粘性廣播中,若是存在就將這些Intent單獨保存到一個列表中,而後處理廣播接收器,上面代碼和註釋寫的很清楚了,廣播註冊不是直接將receiver保存在AMS中,而是先將其封裝到實現IIntentReceiver接口的Binder對象rd中,而後將這個對象放到ReceiverList對象中,這個ReceiverList對象是一個對應receiver的IntentFilter列表,可是這個列表對象也包含了該receiver對象,也就是將receiver以及其對應的IntentFilter列表封裝到了ReceiverList對象中,這樣每一個廣播接收者以及其Action都封裝好了,而後將其放到該應用所在進程的ReceiverList對象列表中,這樣整個廣播註冊就完成了。ide
其實縷清了這個結構就看懂廣播註冊了:首先是一個進程對象ProcessRecord,裏面有一個廣播的列表ArraySet,這個列表表示改進程註冊的全部廣播接收者,每一個ReceiverList對象包含了一個廣播接收者(實現了IIntentReceiver接口的Binder對象)封裝和與該廣播接收者對應的多個Action對應的IntentFilter對象的封裝BroadcastFilter列表,這個ReceiverList對象是將註冊的廣播接收者以及對應的多個Action對應起來,這樣就能查找對應的廣播接收者,怎麼調用咱們下一篇發送廣播會詳細講解。ui
final ProcessRecord getRecordForAppLocked( IApplicationThread thread) {
if (thread == null) {
return null;
}
int appIndex = getLRURecordIndexForAppLocked(thread);
return appIndex >= 0 ? mLruProcesses.get(appIndex) : null;
}
複製代碼
這裏是根據IApplicationThread獲取是否存在了該進程,這裏調用getLRURecordIndexForAppLocked獲取該進程對應的indexthis
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
IBinder threadBinder = thread.asBinder();
// Find the application record.
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
return i;
}
}
return -1;
}
複製代碼
這裏主要經過for循環來從mLruProcesses列表中遍歷是否存在該IApplicationThread,若是存在返回對應的Index,不然返回-1.spa
int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, int allowMode, String name, String callerPackage) {
final int callingUserId = UserHandle.getUserId(callingUid);
if (callingUserId == userId) {
return userId;
}
...
int targetUserId = unsafeConvertIncomingUserLocked(userId);
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
if (mService.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
// If the caller has this permission, they always pass go. And collect $200.
allow = true;
}
...
if (!allow) {
if (userId == UserHandle.USER_CURRENT_OR_SELF) {
// In this case, they would like to just execute as their
// owner user instead of failing.
targetUserId = callingUserId;
} else {
...
}
}
}
...
return targetUserId;
}
複製代碼
這裏主要獲取callingPid等參數對應的用戶id。
// 驅動廣播,全部廣播都應該從這裏走,而後會到processNextBroadcast
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
// mBroadcastsScheduled用來描述AMS是否已經向它所運行在的線程的消息隊列發送了一個類型爲
// BROADCAST_INTENT_MSG的消息。AMS就是經過這個BROADCAST_INTENT_MSG消息類調度保存在無
// 序廣播調度隊列mParallelBroadcasts和有序廣播調度隊列mOrderedBroadcasts中的廣播轉發任務的
if (mBroadcastsScheduled) {// 若是true說明消息隊列已經存在一個類型爲BROADCAST_INTENT_MSG的消息了
return;
}
// 雖然這裏只發送了發送廣播的消息,可是這一步執行完以後就已經標記廣播發送了,所以能夠看出廣播發送和接
// 受是異步的,即廣播發送者將一個廣播發送給AMS後,不會等待AMS將這個廣播轉發給廣播接收者處理
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
複製代碼
瞭解了廣播註冊,廣播註銷就很簡單了,就是從列表中刪除對應的廣播對象封裝。
public void unregisterReceiver(BroadcastReceiver receiver) {
if (mPackageInfo != null) {
IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
getOuterContext(), receiver);
try {
ActivityManagerNative.getDefault().unregisterReceiver(rd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
...
}
}
複製代碼
首先經過LoadedApk.forgetReceiverDispatcher方法獲取與該註銷廣播接收者對應的實現IIntentReceiver接口的Binder對象。
public IIntentReceiver forgetReceiverDispatcher(Context context, BroadcastReceiver r) {
synchronized (mReceivers) {
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
LoadedApk.ReceiverDispatcher rd = null;
if (map != null) {
rd = map.get(r);
if (rd != null) {
map.remove(r);
if (map.size() == 0) {
mReceivers.remove(context);
}
...
return rd.getIIntentReceiver();
}
}
...
}
}
複製代碼
首先去mReceivers中獲取,咱們從上面註冊知道,註冊時會將實現了IIntentReceiver接口的廣播接收者的封裝放到mReceivers保存,因此這裏先去獲取有沒有,註冊了確定是有的,所以將其移除。
public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException {
...
mRemote.transact(UNREGISTER_RECEIVER_TRANSACTION, data, reply, 0);
...
}
複製代碼
上面註冊廣播咱們分析過調用ActivityManagerProxy這裏的方法,而後經過Binder傳遞到AMS中對應的方法中
// 註銷廣播
public void unregisterReceiver(IIntentReceiver receiver) {
...
try {
boolean doTrim = false;
synchronized (this) {
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
// 若是不爲null,說明還沒註銷廣播
if (rl != null) {
final BroadcastRecord r = rl.curBroadcast;
if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
final boolean doNext = r.queue.finishReceiverLocked(
r, r.resultCode, r.resultData, r.resultExtras,
r.resultAbort, false);
if (doNext) {
doTrim = true;
r.queue.processNextBroadcast(false);
}
}
if (rl.app != null) {
// 從廣播接收器對應的進程中移除
rl.app.receivers.remove(rl);
}
// 移除對應廣播過濾器
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
rl.linkedToDeath = false;
rl.receiver.asBinder().unlinkToDeath(rl, 0);
}
}
}
// If we actually concluded any broadcasts, we might now be able
// to trim the recipients' apps from our working set
if (doTrim) {
trimApplications();
return;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
複製代碼
找到對應的註冊廣播對象ReceiverList,而後將其移除。
public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
if (mOrderedBroadcasts.size() > 0) {
final BroadcastRecord r = mOrderedBroadcasts.get(0);
if (r != null && r.receiver == receiver) {
return r;
}
}
return null;
}
複製代碼
從隊列中找到對應的BroadcastRecord對象而後返回。
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
final int state = r.state;
final ActivityInfo receiver = r.curReceiver;
r.state = BroadcastRecord.IDLE;
...
if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
&& r.queue.mOrderedBroadcasts.size() > 0
&& r.queue.mOrderedBroadcasts.get(0) == r) {
ActivityInfo nextReceiver;
if (r.nextReceiver < r.receivers.size()) {
Object obj = r.receivers.get(r.nextReceiver);
nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo) obj : null;
} else {
nextReceiver = null;
}
// Don't do this if the next receive is in the same process as the current one.
if (receiver == null || nextReceiver == null
|| receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
|| !receiver.processName.equals(nextReceiver.processName)) {
if (mService.mServices.hasBackgroundServices(r.userId)) {
r.state = BroadcastRecord.WAITING_SERVICES;
return false;
}
}
}
r.curComponent = null;
// We will process the next receiver right now if this is finishing
// an app receiver (which is always asynchronous) or after we have
// come back from calling a receiver.
return state == BroadcastRecord.APP_RECEIVE
|| state == BroadcastRecord.CALL_DONE_RECEIVE;
}
複製代碼
這裏判斷是否結束廣播接收,若是結束則處理下一條廣播。
這個是處理下一條廣播的,也是廣播的核心部分,這個咱們在下一篇發送廣播時會詳細講解。
void removeReceiverLocked(ReceiverList rl) {
mRegisteredReceivers.remove(rl.receiver.asBinder());
for (int i = rl.size() - 1; i >= 0; i--) {
mReceiverResolver.removeFilter(rl.get(i));
}
}
複製代碼
這個就是經過for循環刪除對應的BroadcastFilter對象,這樣就註銷了廣播。
主要的基本都分析了,還有如下其餘不重要的你們想要了解本身看看代碼。
最後這裏咱們再加一個廣播註冊結構的總結,上面是一個廣播註冊時的結構圖,也就是廣播以及對應的IntentFilter列表封裝,整個過程是由下向上註冊。首先是將BroadcastReceiver封裝成Binder對象IIntentReceiver,將IntentFilter封裝成BroadcastFilter對象,ReceiverList繼承的是ArrayList<BroadcastFilter>,所以它自己就是一個用來盛放BroadcastFilter對象列表的ArrayList對象,同時ReceiverList對象還放入了IntentFilter列表對應的BroadcastReceiver的封裝對象IIntentReceiver,這樣就將BroadcastReceiver和IntentFilter綁定到一塊兒了,而後將ReceiverList放到mRegisteredReceivers中,保存在ActivityManagerService(AMS)中,同時將ReceiverList放置到該廣播所在進程的receivers中,而該進程保存在AMS中的mLruProcesses中,同時將IntentFilter的封裝對象BroadcastReceiver放置到AMS中的mReceiverResolver中,這樣就註冊完成了。
這樣從整個機構來講就很是清楚了,其實裏面還有一些相互引用的狀況,我沒有徹底畫出來,只畫了主要的部分,相對清晰一些。
Android開發羣:192508518
微信公衆帳號:Code-MX
首發地址:www.codemx.cn
注:本文原創,轉載請註明出處,多謝。