廣播這個(gè)篇幅打算用四篇文章來寫玫恳,分別為廣播注冊、廣播處理优俘、廣播的發(fā)送京办,廣播深入細(xì)節(jié)理解,如果都寫到一篇文章會比較長帆焕,所以拆分成四篇來寫惭婿。
第一篇
Android源碼解析---廣播的注冊過程
第二篇
Android源碼解析---廣播的處理過程
第三篇
Android源碼解析---廣播的發(fā)送過程
第四篇
Android源碼解析---廣播深入細(xì)節(jié)理解
想收到廣播(Broadcast),必須先要注冊接收廣播的組件---廣播接收者(receiver)叶雹,廣播接收者的注冊分為動態(tài)注冊和靜態(tài)注冊审孽,而注冊中心就是AMS,AMS再把廣播分發(fā)到各個(gè)廣播接收者(receiver)浑娜。
一個(gè)廣播可以有多個(gè)receiver來接收它佑力,注冊的方式分為兩種,一種是靜態(tài)注冊筋遭,一種是動態(tài)注冊打颤,動態(tài)注冊廣播不是常駐型廣播,也就是說廣播跟隨Activity的生命周期漓滔,在Activity結(jié)束前编饺,需要移除廣播接收器。 靜態(tài)注冊是常駐型响驴,也就是說當(dāng)應(yīng)用程序關(guān)閉后透且,如果有信息廣播來,程序也會被系統(tǒng)調(diào)用自動運(yùn)行豁鲤。
1.1 動態(tài)廣播注冊
動態(tài)注冊是由ContextImpl的registerReceiver方法調(diào)用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) {
//為空表示默認(rèn)為主線程
if (scheduler == null) {
//AMS并不是直接給廣播接收者發(fā)送廣播的秽誊,當(dāng)廣播到達(dá)應(yīng)用程序進(jìn)程的時(shí)候,
//會被封裝成一個(gè)Message,然后push到主線程消息隊(duì)列中琳骡,然后才會給接
//收者處理锅论,你也可以指定一個(gè)處理的Handler,將onReceive()調(diào)度在非主線程執(zhí)行楣号。
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等發(fā)送給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();
}
}
傳進(jìn)來的receiver不是直接發(fā)送給AMS的怒坯,首先會把receiver封裝成一個(gè)IIntentReceiver對象rd,這個(gè)rd是一個(gè)binder本地對象,具備了跨進(jìn)程通信的能力藻懒。mPackageInfo是LoadedApk,LoadedApk這個(gè)類包含了當(dāng)前加載的apk的主要的信息,其中成員變量mReceivers表就記錄了所有動態(tài)注冊的receiver剔猿。
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
rd的獲取有兩種,當(dāng)mPackageInfo存在時(shí)候嬉荆,就通過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傳進(jìn)來的是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 {
//檢查廣播分發(fā)者的context、handler是否一致
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
這個(gè)方法內(nèi)部維護(hù)了一張表 ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null员寇,每一個(gè)廣播接收者對應(yīng)一個(gè)ReceiverDispatcher(廣播分發(fā)者)弄慰,并且把最后把這個(gè)map以Context為key存在mReceivers中,而這個(gè)Context跟廣播的發(fā)送方有關(guān)系第美,如果是在Activity中發(fā)送的蝶锋,這個(gè)Context就指向與這個(gè)Activity,如果是在Service中發(fā)送的,這個(gè)Context就指向了這個(gè)Service什往。那么這個(gè)廣播分發(fā)者有什么用呢扳缕?用map來存儲,表示ReceiverDispatcher跟BroadcastReceiver是一一對應(yīng)的别威,每個(gè)廣播接收者對應(yīng)一個(gè)廣播分發(fā)者躯舔, 當(dāng)AMS向app發(fā)送廣播時(shí)會調(diào)用到app進(jìn)程的廣播分發(fā)者,然后再將廣播以message形式post到app的主線程省古,來執(zhí)行onReceive()方法粥庄。
假設(shè)我的app只有兩個(gè)Activity(HomeActivity和DetailActivity),這個(gè)app被打包成xxx.apk豺妓,那么在內(nèi)存中惜互,這個(gè)xxx.apk由LoadedApk來描述,如果HomeActivity和DetailActivity都注冊了廣播琳拭,那么LoadedApk內(nèi)部維持的mReceivers的長度就為2⊙刀眩現(xiàn)在梳理一下上面的代碼,當(dāng)一個(gè)BroadcastReceiver要注冊的時(shí)候白嘁,會優(yōu)先使用Context查看一下坑鱼,這個(gè)組件有沒有注冊過廣播,如果有就取出來絮缅,類型是一個(gè)ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> 的map鲁沥。如果沒有,就把map創(chuàng)建出來耕魄,并且存到mReceivers中黍析。有了map之后,需要把ReceiverDispatcher(廣播分發(fā)者)存到map里面去屎开。第一次注冊時(shí)候阐枣,肯定走的是if(rd==null)的代碼塊里面马靠,這樣rd對象被創(chuàng)建出來。現(xiàn)在分析一下ReceiverDispatcher這個(gè)類蔼两。
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;
//表示哪個(gè)發(fā)送的廣播
mContext = context;
//主線程
mActivityThread = activityThread;
.......
}
.......
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}
.......
}
在內(nèi)部會創(chuàng)建InnerReceiver甩鳄,InnerReceiver是ReceiverDispatcher的內(nèi)部類,是一個(gè)實(shí)現(xiàn)Binder的本地對象额划,前面也說過了妙啃,最終是將一個(gè)InnerReceiver對象注冊到了AMS中。
OK,繞了這么一大圈子俊戳,其實(shí)就是為了封裝一個(gè)InnerReceiver用于和AMS通信揖赴,我也不知道谷歌這幫程序員怎么想的,有點(diǎn)麻煩抑胎。忽略跨進(jìn)程的代碼燥滑,現(xiàn)在由用戶進(jìn)程走到SystemServer進(jìn)程了,即走到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獲取當(dāng)前進(jìn)程對象
callerApp = getRecordForAppLocked(caller);
//進(jìn)程還沒創(chuàng)建铭拧,直接拋出異常
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中,先把粘性廣播帥選出來恃锉,放進(jìn)stickyIntents中
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
//從mStickyBroadcasts中查看用戶的sticky Intent,mStickyBroadcasts存了系統(tǒng)所有的粘性廣播
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())) {
// 進(jìn)程不存在(死亡了),也是不能注冊成功的
return null;
}
//mRegisteredReceivers表存了所有動態(tài)注冊的廣播接收者破托,
//由receiver作為key肪跋,獲取到ReceiverList,為什么是ReceiverList土砂,
//而不是一個(gè)Receiver呢州既,因?yàn)橐粋€(gè)廣播可能會有多個(gè)接收者,
//最好整成一個(gè)隊(duì)列或者鏈表的形式瘟芝,而ReceiverList繼承ArrayList易桃,滿足這個(gè)需求。
//每個(gè)ReceiverList都對應(yīng)著Client端的一個(gè)ReceiverDispatcher锌俱。
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
//把廣播接收者列表加到這個(gè)進(jìn)程對象的receivers中
rl.app.receivers.add(rl);
} else {
try {
//進(jìn)程不存在晤郑,注冊死亡通知
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
//新創(chuàng)建的接收者隊(duì)列,添加到已注冊廣播隊(duì)列贸宏。
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內(nèi)部造寝,廣播接收者實(shí)際上是BroadcastFilter來描述的,
//由filter等參數(shù)創(chuàng)建BroadcastFilter對象吭练,并添加到接收者隊(duì)列,
//注意只有registerReceiver()過程才會創(chuàng)建BroadcastFilter,也就是該對
//象用于動態(tài)注冊的廣播Receiver;诫龙,靜態(tài)的接收者對象不是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);
//如果是粘性廣播鲫咽,創(chuàng)建BroadcastRecord签赃,并添加到
//BroadcastQueue的并行廣播隊(duì)列(mParallelBroadcasts)谷异,
//注冊后調(diào)用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();
}
}
//返回值是一個(gè)Intent
return sticky;
}
}
總結(jié)一下:動態(tài)注冊是調(diào)用registerReceiver來注冊的锦聊,大致流程如下:
在Android系統(tǒng)中歹嘹,系統(tǒng)每加載一個(gè)apk,就會有一個(gè)LoadedApk對象孔庭。而每個(gè)LoadedApk對象里會有一張名字為mReceivers的HashMap尺上,用來記錄每個(gè)apk里面動態(tài)注冊了那些廣播接收者。mReceivers的類型是ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>圆到,泛型中的Context代表是誰注冊的怎抛,后面的值也是個(gè)map ,ArrayMap<BroadcastReceiver, ReceiverDispatcher>表示BroadcastReceiver與ReceiverDispatcher一一對應(yīng)芽淡,一個(gè)BroadcastReceiver對應(yīng)一個(gè)ReceiverDispatcher马绝,通過這個(gè)定義可以知道,不管一個(gè)Activity注冊了多少BroadcastReceiver吐绵,ReceiverDispatcher只有一個(gè)迹淌。ReceiverDispatcher內(nèi)部有一個(gè)InnerReceiver的Binder對象河绽,最終是把這個(gè)InnerReceiver發(fā)送給了AMS,AMS內(nèi)部也維護(hù)了一張表mRegisteredReceivers己单,用來記錄所有動態(tài)注冊的接收者,首先會根據(jù)傳進(jìn)來的InnerReceiver對象取出來一個(gè)ReceiverList耙饰,ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder())纹笼,所以實(shí)質(zhì)上每個(gè)ReceiverList都對應(yīng)著Client端的一個(gè)ReceiverDispatcher。最后把創(chuàng)建的對象BroadcastFilter(AMS內(nèi)部廣播接收者用BroadcastFilter來表示)加到ReceiverList接收者隊(duì)列中苟跪,注冊完畢廷痘!
1.2 靜態(tài)廣播注冊
靜態(tài)注冊就是在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>
它們的信息會在系統(tǒng)啟動時(shí)件已,由PackageManagerService(PMS)解析(在該類的構(gòu)造方法中會對各個(gè)應(yīng)用安裝目錄的apk文件進(jìn)行掃描解析)并記錄下來笋额。
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);
}
經(jīng)過上面的解析receiver就被保存到了owner.receivers中去了。然后AM會調(diào)用PMS的接口來查詢“和intent匹配的組件”時(shí)篷扩,PMS內(nèi)部就會去查詢當(dāng)初記錄下來的數(shù)據(jù)兄猩,并把結(jié)果返回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();
}
}
因?yàn)樯婕癙MS鉴未,這段邏輯想寫清楚篇幅會比較大枢冤,所以,不深入討論铜秆,以上關(guān)于廣播的動態(tài)注冊和靜態(tài)注冊就介紹完了淹真。