ActivityManagerService廣播注冊與發(fā)送示例解析
引言
最近,幫同事解決了兩個問題,一個問題是 app 接收開機廣播的速度太慢,另一個問題是app有時無法接收到廣播。同事不知道如何解決這個問題,是因為他們不了解廣播發(fā)送超時的原理。
很早的時候,我就研究過廣播的代碼,由于工作比較忙,再加上我這個人比較懶,因此沒有寫成文章。由于最近這段時間,工作和生活都不是很如意,于是我想靜下心來寫寫東西,因此就有了這篇文章。
在看本文之前,請讀者自行了解 普通的廣播、粘性(sticky)廣播、有序廣播 的使用。
注冊廣播接收器
廣播接收器可以分為動態(tài)和靜態(tài),靜態(tài)廣播接收器就是在 AndroidManifest.xml 中注冊的,而動態(tài)的廣播接收器是在代碼中通過 Context#registerReceiver() 注冊的。
靜態(tài)廣播接收器,在發(fā)送廣播時,服務(wù)端會從 PKMS 中收集,而動態(tài)的廣播接收器,需要接收方發(fā)送給服務(wù)端。因此,下面只分析動態(tài)廣播接收器的注冊過程
// ContextImpl.java
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, user.getIdentifier(),
filter, broadcastPermission, scheduler, getOuterContext(), 0);
}
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
// 默認主線程 Handler
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
// 1. 獲取 IIntentReceiver 對象
// 其實這里獲取的就是一個 Binder 對象,用于注冊給 AMS,從而接收廣播信息的回調(diào)
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
// ...
}
}
try {
// 2. 向 AMS 注冊 IIntentReceiver
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
getAttributionSource());
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
廣播接收方注冊接收器的過程如下
- 獲取 IIntentReceiver 對象,它是一個 Binder 對象,其實就是一個 Binder 回調(diào)。
- 向服務(wù)端 AMS 注冊 IIntentReceiver 對象,用于接收廣播消息的回調(diào)。
當接收方收到來自服務(wù)端的廣播消息后,會通過 IIntentReceiver 對象,調(diào)用 BroadcastReceiver#onReceive() 來處理廣播。
現(xiàn)在來看下服務(wù)端是如何完成廣播接收器的注冊工作的
// AcitityManagerService.java
public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
String callerFeatureId, String receiverId, IIntentReceiver receiver,
IntentFilter filter, String permission, int userId, int flags) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
final boolean visibleToInstantApps
= (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
int callingUid;
int callingPid;
boolean instantApp;
synchronized(this) {
// 確保接收方進程存在,并該進程的 uid 和 pid
if (caller != null) {
callerApp = getRecordForAppLOSP(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 != SYSTEM_UID
&& !callerApp.getPkgList().containsKey(callerPackage)
&& !"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.getPid();
} 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);
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) };
// 遍歷 IntentFilter 保存的所有 action,匹配相應(yīng)的 sticky 廣播,并保存到 stickyIntents
// 從這里可以看出,可以先發(fā)送 sticky 廣播,然后再注冊 sticky 廣播接收器
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);
}
}
}
}
}
// 剛才是用 action 匹配 sticky 廣播,現(xiàn)在使用 IntentFilter 再次過濾
// 過濾后的 sticky 廣播 ,保存到 allSticky
// 因此 allSticky 保存的才是最終完美匹配到的 sticky 廣播
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);
// Don't provided intents that aren't available to instant apps.
if (instantApp &&
(intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
continue;
}
// 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.
// 從這里可以看出,如果注冊的廣播接收器為 null,那么表示要獲取最近一次 sticky 廣播的數(shù)據(jù)
Intent sticky = allSticky != null ? allSticky.get(0) : null;
if (receiver == null) {
return sticky;
}
// ...
synchronized (this) {
IApplicationThread thread;
// 注意學(xué)會這里的操作,如何判斷原來的進程已經(jīng)死亡
if (callerApp != null && ((thread = callerApp.getThread()) == null
|| thread.asBinder() != caller.asBinder())) {
// Original caller already died
return null;
}
// 1. mRegisteredReceivers 建立客戶端與服務(wù)端的廣播接收器的映射
// 客戶端注冊的廣播接收器是 IIntentReceiver, 而服務(wù)端的是 ReceiverList
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
throw new IllegalStateException("Too many receivers, total of "
+ totalReceiversForApp + ", registered for pid: "
+ rl.pid + ", callerPackage: " + callerPackage);
}
// ProcessRecord#mReceivers 保存 ReceiverList
rl.app.mReceivers.addReceiver(rl);
} else {
// ...
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else {
// ...
}
// 2. 創(chuàng)建服務(wù)端的廣播過濾器 BroadcastFilter,并保存到 mReceiverResolver
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps);
if (rl.containsFilter(filter)) {
Slog.w(TAG, "Receiver with filter " + filter
+ " already registered for pid " + rl.pid
+ ", callerPackage is " + callerPackage);
} else {
// ReceiverList 是 ArrayList 子類,之所以用一個列表保存 BroadcastFilter
// 是因為在注冊廣播接收器時,可以為同一個廣播接收器匹配多個過濾器
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
// 解析過濾器的數(shù)據(jù),然后用相應(yīng)的數(shù)據(jù)結(jié)構(gòu)保存
mReceiverResolver.addFilter(bf);
}
// Enqueue broadcasts for all existing stickies that match
// this filter.
// 注意,這里處理的情況是,注冊的 sticky 廣播接收器不為 null
// 那么把匹配到的 sticky 廣播,發(fā)送給這個廣播接收器
// 是不是非常有意思,注冊 sticky 廣播接收器,就能立即收到廣播,這得益于 sticky 廣播被緩存
if (allSticky != null) {
// 很奇怪,BroadcastFilter 怎么是廣播接收器呢?
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, null, -1, -1, false, null, null, null, OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1, false, null,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
服務(wù)端對于動態(tài)注冊的廣播接收器的處理過程如下
- 使用 mRegisteredReceivers 建立客戶端與服務(wù)端的廣播接收器的映射。ReceiverList 代表服務(wù)端的廣播接收器,IIntentReceiver 代表客戶端的廣播接收器。
- 使用客戶端的 IntentFilter , 創(chuàng)建服務(wù)端的廣播過濾器 BroadcastFilter,并保存到 mReceiverResolver。注意,這一步中,ReceiverList 和 BroadcastFilter 互相保存了引用。
這些數(shù)據(jù)結(jié)構(gòu)都是相互關(guān)聯(lián)的,有何種用意呢?當發(fā)送方發(fā)送廣播到 AMS,AMS 會使用 mReceiverResolver 匹配 BroadcastFilter,BroadcastFilter 找到 ReceiverList,ReceiverList 找到 IIntentReceiver,IIntentReceiver 發(fā)送廣播給接收方。
另外,由于 sticky 廣播是會被緩存的,當注冊 sticky 廣播的接收器時,有以下兩種處理方式
- 如果注冊的廣播接收器為 null,那么會返回最近的一次廣播數(shù)據(jù)給接收方。
- 如果注冊的廣播接收器不為null,那么會把匹配到的 sticky 廣播發(fā)送給接收方的廣播接收器,也就是會調(diào)用 BroadcastReceiver#onReceive()。
發(fā)送廣播
發(fā)送廣播的方法,有多個重載方法,挑選一個最簡單的來分析,如下
// ContextImpl.java
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
null, AppOpsManager.OP_NONE, null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
// ActivityManagerService.java
public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, String[] excludedPermissions,
String[] excludedPackages, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLOSP(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
return broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
serialized, sticky, callingPid, callingUid, callingUid, callingPid, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, String[] excludedPermissions,
String[] excludedPackages, int appOp, Bundle bOptions, boolean ordered,
boolean sticky, int callingPid,
int callingUid, int realCallingUid, int realCallingPid, int userId) {
return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
excludedPermissions, excludedPackages, appOp, bOptions, ordered, sticky, callingPid,
callingUid, realCallingUid, realCallingPid, userId,
false /* allowBackgroundActivityStarts */,
null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastAllowList */);
}
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
boolean allowBackgroundActivityStarts,
@Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastAllowList) {
// 克隆一個 Intent,防止原始 intent 數(shù)據(jù)被修改
intent = new Intent(intent);
// ...
// If we have not finished booting, don't allow this to launch new processes.
// AMS 還沒有啟動完成前,廣播只能發(fā)送給動態(tài)注冊的廣播接收器,這樣可以防止拉起新的進程
if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
// ...
final String action = intent.getAction();
BroadcastOptions brOptions = null;
// bOptions 可以解決發(fā)送廣播的一些限制,例如從后臺啟動Activity,只有系統(tǒng) app 才能用到的 API
if (bOptions != null) {
// ...
}
// 限制受保護的廣播,只能由系統(tǒng)代碼發(fā)送
final boolean isProtectedBroadcast;
try {
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
return ActivityManager.BROADCAST_SUCCESS;
}
final boolean isCallerSystem;
switch (UserHandle.getAppId(callingUid)) {
case ROOT_UID:
case SYSTEM_UID:
case PHONE_UID:
case BLUETOOTH_UID:
case NFC_UID:
case SE_UID:
case NETWORK_STACK_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.isPersistent();
break;
}
if (!isCallerSystem) {
if (isProtectedBroadcast) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from pid="
+ callingPid + ", uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
// ...
}
}
boolean timeoutExempt = false;
if (action != null) {
// 如果系統(tǒng)配置文件中允許發(fā)送這個后臺廣播,那么添加 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND 標志位
// 例如 frameworks/base/data/etc/framework-sysconfig.xml 允許發(fā)送如下后臺廣播
// <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
if (getBackgroundLaunchBroadcasts().contains(action)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
}
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
switch (action) {
// 對一些特殊的廣播進行處理...
}
}
// 1. 緩存 sticky 廣播到 mStickyBroadcasts
if (sticky) {
// 檢查權(quán)限
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
+ callingPid + ", uid=" + callingUid
+ " requires " + android.Manifest.permission.BROADCAST_STICKY;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
if (requiredPermissions != null && requiredPermissions.length > 0) {
Slog.w(TAG, "Can't broadcast sticky intent " + intent
+ " and enforce permissions " + Arrays.toString(requiredPermissions));
return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
}
if (intent.getComponent() != null) {
throw new SecurityException(
"Sticky broadcasts can't target a specific component");
}
// 確保使用 userId 發(fā)送的 sticky 廣播,不會與使用 USER_ALL 發(fā)送的 sticky 廣播有沖突
if (userId != UserHandle.USER_ALL) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
UserHandle.USER_ALL);
if (stickies != null) {
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list != null) {
int N = list.size();
int i;
for (i=0; i<N; i++) {
if (intent.filterEquals(list.get(i))) {
throw new IllegalArgumentException(
"Sticky broadcast " + intent + " for user "
+ userId + " conflicts with existing global broadcast");
}
}
}
}
}
// 獲取 userId 對應(yīng)的 sticky 廣播緩存,并把這個stikcy廣播添加/替換到緩存中
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null) {
stickies = new ArrayMap<>();
mStickyBroadcasts.put(userId, stickies);
}
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null) {
list = new ArrayList<>();
stickies.put(intent.getAction(), list);
}
final int stickiesCount = list.size();
int i;
// 存在就替換
for (i = 0; i < stickiesCount; i++) {
if (intent.filterEquals(list.get(i))) {
// This sticky already exists, replace it.
list.set(i, new Intent(intent));
break;
}
}
// 不存在就添加
if (i >= stickiesCount) {
list.add(new Intent(intent));
}
}
int[] users;
if (userId == UserHandle.USER_ALL) {
// 如果以 USER_ALL 名字發(fā)送,那么獲取所有的啟動的 user
users = mUserController.getStartedUserArray();
} else {
users = new int[] {userId};
}
// Figure out who all will receive this broadcast.
// 現(xiàn)在開始找出誰需要接收這個廣播
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// 2. 收集動態(tài)和靜態(tài)廣播接收器
// Intent.FLAG_RECEIVER_REGISTERED_ONLY 表示廣播只能發(fā)送給動態(tài)注冊的廣播接收器
// 沒有指定這個標志位,那么就需要收集靜態(tài)注冊的廣播接收器
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
// receivers 此時保存的是靜態(tài)廣播接收器
receivers = collectReceiverComponents(
intent, resolvedType, callingUid, users, broadcastAllowList);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// ...以 USER_ALL 身份,從 shell 發(fā)送的廣播,那么需要獲取所有用戶注冊的廣播接收器
} else {
// 收集單個用戶注冊的動態(tài)廣播接收器
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
// 是否替換即將發(fā)送的廣播
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
+ " replacePending=" + replacePending);
// broadcastAllowList 是能接收廣播 app 白名單
// 如果收集的動態(tài)注冊的廣播接收器,不屬于白名單中的 app,那么移除它
if (registeredReceivers != null && broadcastAllowList != null) {
for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
final int owningAppId = UserHandle.getAppId(registeredReceivers.get(i).owningUid);
if (owningAppId >= Process.FIRST_APPLICATION_UID
&& Arrays.binarySearch(broadcastAllowList, owningAppId) < 0) {
registeredReceivers.remove(i);
}
}
}
// 3. 對于非有序廣播(包括sticky廣播),先"并行"地發(fā)送給動態(tài)廣播接收器
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered,
sticky, false, userId, allowBackgroundActivityStarts,
backgroundActivityStartsToken, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending
&& (queue.replaceParallelBroadcastLocked(r) != null);
// Note: We assume resultTo is null for non-ordered broadcasts.
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
// 注意,對于發(fā)送非有序廣播,當把廣播發(fā)送給動態(tài)接收器后, NR 重置為 0
NR = 0;
}
// 4. 對于有序廣播,按照優(yōu)先級從高到低的順序,合并靜態(tài)和動態(tài)廣播接收器到 receivers
int ir = 0;
// receivers 收集的是靜態(tài)注冊的廣播接收器
if (receivers != null) {
// ...
// NT 表示靜態(tài)廣播接收器的數(shù)量
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
// 注意 NR 的值,前面發(fā)送非有序廣播給動態(tài)接收器時,NR 重置為 0
// 如果此時 NR 還不為 0, 那么表示發(fā)送的是有序廣播
// 那么根據(jù)廣播的優(yōu)先級,按照從高到低的順序,把動態(tài)廣播接收器和靜態(tài)廣播接收器,合并到 receivers
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, receivers);
}
// 5. “序列化”地發(fā)送廣播
// 注意,這里分兩種情況
// 如果發(fā)送的非有序廣播,那么 receivers 只保存了靜態(tài)注冊的廣播接收器
// 如果發(fā)送的是有序廣播,那么 receivers 保存了靜態(tài)和動態(tài)注冊的廣播接收器
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
receivers, resultTo, resultCode, resultData, resultExtras,
ordered, sticky, false, userId, allowBackgroundActivityStarts,
backgroundActivityStartsToken, timeoutExempt);
final BroadcastRecord oldRecord =
replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
if (oldRecord != null) {
// 處理替換廣播的情況 ....
} else {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
} else {
// 沒有找到接收廣播的接收器,簡單記錄下這個發(fā)送廣播的操作 ...
}
return ActivityManager.BROADCAST_SUCCESS;
}
這段代碼設(shè)計的并不是很好,很多細節(jié)都雜糅到一個方法中。對于一些細節(jié),我進行了詳細的注釋,有興趣的讀者可以自行研究,而本文只關(guān)心發(fā)送廣播的主要流程。
我不打算按照代碼的邏輯來講解流程,我自己總結(jié)了一套流程。
對于非有序廣播(包括 sticky 廣播),發(fā)送流程如下
- 把廣播“并行”地發(fā)送給動態(tài)廣播接收器。
- 把廣播“串行”地發(fā)送給靜態(tài)廣播接收器。
為何要把非有序廣播(包括 sticky 廣播)優(yōu)先發(fā)送給動態(tài)接收器?最簡單的理由就是,不需要先拉起進程!因為"快”,所以先發(fā)送。
對于有序廣播,發(fā)送的流程如下
- 按照廣播的優(yōu)先級,從高到低,把動態(tài)和靜態(tài)廣播接收器,合并到一起。
- “串行”發(fā)送廣播給所有的接收器。
有序廣播,為何不先發(fā)送給動態(tài)接收器呢?因為它強調(diào)一個“有序”,所以要根據(jù)優(yōu)先級來發(fā)送。
對于 sticky 廣播,由于它的特性,是需要對它的廣播 Intent 進行緩存的。根據(jù)前面注冊廣播接收器的分析,當注冊的廣播接收器匹配到緩存的 sticky 廣播 Intent,那么會立即返回數(shù)據(jù)給接收方,無論是通過函數(shù)的返回值,還是直接調(diào)用 BroadcastReceiver#onReceive()。
結(jié)束
本文從整體上介紹了廣播接收器的注冊流程、發(fā)送廣播的流程。而發(fā)送廣播有“并行”的方式,也有“串行”的方式,限于篇幅原因,留到下一篇文章分析,更多關(guān)于ActivityManagerService廣播注冊發(fā)送的資料請關(guān)注腳本之家其它相關(guān)文章!
- Android registerForActivityResult動態(tài)申請權(quán)限案例詳解
- ActivityManagerService廣播并行發(fā)送與串行發(fā)送示例解析
- ActivityManagerService之Service啟動過程解析
- Android面向單Activity開發(fā)示例解析
- Vue3源碼分析reactivity實現(xiàn)方法示例
- vue3源碼分析reactivity實現(xiàn)原理
- Android10 App啟動Activity源碼分析
- Android?Activity共享元素動畫示例解析
- Android?registerForActivityResult新用法實現(xiàn)兩個Activity間數(shù)據(jù)傳遞
相關(guān)文章
橫豎屏切換導(dǎo)致頁面頻繁重啟screenLayout解析
這篇文章主要為大家介紹了橫豎屏切換導(dǎo)致頁面頻繁重啟screenLayout解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Android數(shù)據(jù)傳輸中的參數(shù)加密代碼示例
這篇文章主要介紹了Android數(shù)據(jù)傳輸中的參數(shù)加密代碼示例,具有一定參考價值,需要的朋友可以了解下。2017-11-11
Kotlin 封裝萬能SharedPreferences存取任何類型詳解
這篇文章主要介紹了Kotlin 封裝萬能SharedPreferences存取任何類型詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05
Android實現(xiàn)簡單的下拉刷新pulltorefresh
這篇文章主要為大家詳細介紹了Android實現(xiàn)簡單的下拉刷新pulltorefresh的相關(guān)代碼,具有一定的實用性和操作價值,感興趣的小伙伴們可以參考一下2016-07-07
Android開發(fā)入門環(huán)境快速搭建實戰(zhàn)教程
最近想重新學(xué)習下Android,學(xué)習之前開發(fā)環(huán)境的搭建是個首先要解決的問題,所以下面這篇文章主要給大家介紹了Android開發(fā)環(huán)境搭建的相關(guān)資料,文中將實現(xiàn)的步驟一步步介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧。2017-11-11
Android實戰(zhàn)打飛機游戲之子彈生成與碰撞以及爆炸效果(5)
這篇文章主要為大家詳細介紹了Android實戰(zhàn)打飛機游戲之子彈生成與碰撞以及爆炸效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-07-07
Android編程之利用服務(wù)實現(xiàn)電話監(jiān)聽的方法
這篇文章主要介紹了Android編程之利用服務(wù)實現(xiàn)電話監(jiān)聽的方法,較為詳細的分析了Android基于服務(wù)實現(xiàn)針對電話監(jiān)聽的具體步驟與相關(guān)實現(xiàn)技巧,需要的朋友可以參考下2015-11-11

