廣播這個篇幅打算用四篇文章來寫,分別為廣播注冊、廣播處理、廣播的發送,廣播深入細節理解,如果都寫到一篇文章會比較長,所以拆分成四篇來寫。
第一篇
Android源碼解析---廣播的注冊過程
第二篇
Android源碼解析---廣播的處理過程
第三篇
Android源碼解析---廣播的發送過程
第四篇
Android源碼解析---廣播深入細節理解
想收到廣播(Broadcast),必須先要注冊接收廣播的組件---廣播接收者(receiver),廣播接收者的注冊分為動態注冊和靜態注冊,而注冊中心就是AMS,AMS再把廣播分發到各個廣播接收者(receiver)。
一個廣播可以有多個receiver來接收它,注冊的方式分為兩種,一種是靜態注冊,一種是動態注冊,動態注冊廣播不是常駐型廣播,也就是說廣播跟隨Activity的生命周期,在Activity結束前,需要移除廣播接收器。 靜態注冊是常駐型,也就是說當應用程序關閉后,如果有信息廣播來,程序也會被系統調用自動運行。
1.1 動態廣播注冊
動態注冊是由ContextImpl的registerReceiver方法調用registerReceiverInternal來注冊的
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) {
//為空表示默認為主線程
if (scheduler == null) {
//AMS并不是直接給廣播接收者發送廣播的,當廣播到達應用程序進程的時候,
//會被封裝成一個Message,然后push到主線程消息隊列中,然后才會給接
//收者處理,你也可以指定一個處理的Handler,將onReceive()調度在非主線程執行。
scheduler = mMainThread.getHandler();
}
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 {
//將rd,filter等發送給AMS
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
傳進來的receiver不是直接發送給AMS的,首先會把receiver封裝成一個IIntentReceiver對象rd,這個rd是一個binder本地對象,具備了跨進程通信的能力。mPackageInfo是LoadedApk,LoadedApk這個類包含了當前加載的apk的主要的信息,其中成員變量mReceivers表就記錄了所有動態注冊的receiver。
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
rd的獲取有兩種,當mPackageInfo存在時候,就通過mPackageInfo.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;
//registered傳進來的是true
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 ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
//檢查廣播分發者的context、handler是否一致
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
這個方法內部維護了一張表 ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null,每一個廣播接收者對應一個ReceiverDispatcher(廣播分發者),并且把最后把這個map以Context為key存在mReceivers中,而這個Context跟廣播的發送方有關系,如果是在Activity中發送的,這個Context就指向與這個Activity,如果是在Service中發送的,這個Context就指向了這個Service。那么這個廣播分發者有什么用呢?用map來存儲,表示ReceiverDispatcher跟BroadcastReceiver是一一對應的,每個廣播接收者對應一個廣播分發者, 當AMS向app發送廣播時會調用到app進程的廣播分發者,然后再將廣播以message形式post到app的主線程,來執行onReceive()方法。
假設我的app只有兩個Activity(HomeActivity和DetailActivity),這個app被打包成xxx.apk,那么在內存中,這個xxx.apk由LoadedApk來描述,如果HomeActivity和DetailActivity都注冊了廣播,那么LoadedApk內部維持的mReceivers的長度就為2。現在梳理一下上面的代碼,當一個BroadcastReceiver要注冊的時候,會優先使用Context查看一下,這個組件有沒有注冊過廣播,如果有就取出來,類型是一個ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> 的map。如果沒有,就把map創建出來,并且存到mReceivers中。有了map之后,需要把ReceiverDispatcher(廣播分發者)存到map里面去。第一次注冊時候,肯定走的是if(rd==null)的代碼塊里面,這樣rd對象被創建出來。現在分析一下ReceiverDispatcher這個類。
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;
}
.......
final IIntentReceiver.Stub mIIntentReceiver;
final BroadcastReceiver mReceiver;
final Context mContext;
final Handler mActivityThread;
.......
ReceiverDispatcher(BroadcastReceiver receiver, Context context,
Handler activityThread, Instrumentation instrumentation,
boolean registered) {
if (activityThread == null) {
throw new NullPointerException("Handler must not be null");
}
mIIntentReceiver = new InnerReceiver(this, !registered);
//廣播接收者
mReceiver = receiver;
//表示哪個發送的廣播
mContext = context;
//主線程
mActivityThread = activityThread;
.......
}
.......
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}
.......
}
在內部會創建InnerReceiver,InnerReceiver是ReceiverDispatcher的內部類,是一個實現Binder的本地對象,前面也說過了,最終是將一個InnerReceiver對象注冊到了AMS中。
OK,繞了這么一大圈子,其實就是為了封裝一個InnerReceiver用于和AMS通信,我也不知道谷歌這幫程序員怎么想的,有點麻煩。忽略跨進程的代碼,現在由用戶進程走到SystemServer進程了,即走到AMS的registerReceiver方法。
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
int callingUid;
int callingPid;
synchronized(this) {
if (caller != null) {
//由caller獲取當前進程對象
callerApp = getRecordForAppLocked(caller);
//進程還沒創建,直接拋出異常
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
//獲取IntentFilter中的action
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {
ArrayList<String> noAction = new ArrayList<String>(1);
noAction.add(null);
actions = noAction.iterator();
}
//從actions中,先把粘性廣播帥選出來,放進stickyIntents中
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
//從mStickyBroadcasts中查看用戶的sticky Intent,mStickyBroadcasts存了系統所有的粘性廣播
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);
}
}
}
}
}
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);
// If intent has scheme "content", it will need to acccess
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
// 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);
//如果receiver為空,就直接返回了
if (receiver == null) {
return sticky;
}
synchronized (this) {
if (callerApp != null && (callerApp.thread == null
|| callerApp.thread.asBinder() != caller.asBinder())) {
// 進程不存在(死亡了),也是不能注冊成功的
return null;
}
//mRegisteredReceivers表存了所有動態注冊的廣播接收者,
//由receiver作為key,獲取到ReceiverList,為什么是ReceiverList,
//而不是一個Receiver呢,因為一個廣播可能會有多個接收者,
//最好整成一個隊列或者鏈表的形式,而ReceiverList繼承ArrayList,滿足這個需求。
//每個ReceiverList都對應著Client端的一個ReceiverDispatcher。
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
//把廣播接收者列表加到這個進程對象的receivers中
rl.app.receivers.add(rl);
} else {
try {
//進程不存在,注冊死亡通知
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
//新創建的接收者隊列,添加到已注冊廣播隊列。
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);
} else if (rl.pid != callingPid) {
throw new IllegalArgumentException(
"Receiver requested to register for pid " + callingPid
+ " was previously registered for pid " + rl.pid);
} else if (rl.userId != userId) {
throw new IllegalArgumentException(
"Receiver requested to register for user " + userId
+ " was previously registered for user " + rl.userId);
}
//在AMS內部,廣播接收者實際上是BroadcastFilter來描述的,
//由filter等參數創建BroadcastFilter對象,并添加到接收者隊列,
//注意只有registerReceiver()過程才會創建BroadcastFilter,也就是該對
//象用于動態注冊的廣播Receiver;,靜態的接收者對象不是BroadcastFilter。
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
mReceiverResolver.addFilter(bf);
//如果是粘性廣播,創建BroadcastRecord,并添加到
//BroadcastQueue的并行廣播隊列(mParallelBroadcasts),
//注冊后調用AMS來盡快處理該廣播。
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, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
//返回值是一個Intent
return sticky;
}
}
總結一下:動態注冊是調用registerReceiver來注冊的,大致流程如下:
在Android系統中,系統每加載一個apk,就會有一個LoadedApk對象。而每個LoadedApk對象里會有一張名字為mReceivers的HashMap,用來記錄每個apk里面動態注冊了那些廣播接收者。mReceivers的類型是ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>,泛型中的Context代表是誰注冊的,后面的值也是個map ,ArrayMap<BroadcastReceiver, ReceiverDispatcher>表示BroadcastReceiver與ReceiverDispatcher一一對應,一個BroadcastReceiver對應一個ReceiverDispatcher,通過這個定義可以知道,不管一個Activity注冊了多少BroadcastReceiver,ReceiverDispatcher只有一個。ReceiverDispatcher內部有一個InnerReceiver的Binder對象,最終是把這個InnerReceiver發送給了AMS,AMS內部也維護了一張表mRegisteredReceivers,用來記錄所有動態注冊的接收者,首先會根據傳進來的InnerReceiver對象取出來一個ReceiverList,ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()),所以實質上每個ReceiverList都對應著Client端的一個ReceiverDispatcher。最后把創建的對象BroadcastFilter(AMS內部廣播接收者用BroadcastFilter來表示)加到ReceiverList接收者隊列中,注冊完畢!
1.2 靜態廣播注冊
靜態注冊就是在manifest中注冊。
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
它們的信息會在系統啟動時,由PackageManagerService(PMS)解析(在該類的構造方法中會對各個應用安裝目錄的apk文件進行掃描解析)并記錄下來。
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
}
經過上面的解析receiver就被保存到了owner.receivers中去了。然后AM會調用PMS的接口來查詢“和intent匹配的組件”時,PMS內部就會去查詢當初記錄下來的數據,并把結果返回AMS。
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentReceiversInternal(intent, resolvedType, flags, userId));
}
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForResolve(flags, userId, intent);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
userId);
}
return Collections.emptyList();
}
}
因為涉及PMS,這段邏輯想寫清楚篇幅會比較大,所以,不深入討論,以上關于廣播的動態注冊和靜態注冊就介紹完了。