深入Android系统(十一)AMS-4-广播管理
BroadcastReceiver 管理
Broadcast
实质上是提供了一种更灵活的使用Intent
的方式。
无论是启动Activity
,还是Service
,其实都是通过Intent
的发送和过滤来调用应用中的功能,但是Activity
、Service
、ContentProvider
所完成的功能是固定的
而对于BroadcastReceiver
可以做任意的事情,同时广播的发送和接收方式也更加丰富灵活
理解BroadcastReceiver
BroadcastReceiver
是一种很简单的组件,甚至在ActivityThread
中都没有管理它的数据结构。
BroadcastReceiver
的本质就是通过Intent
来执行应用中的一个方法,在应用中并不需要一个长期存在的对象
虽然应用中没有专门的数据结构来管理BroadcastReceiver
,但是在AMS
中还是需要的,因为BroadcastReceiver
可以在运行时动态向AMS
注册,AMS
中需要有数据结构来管理动态接收者
BroadcastReceiver
可以通过两种方式接收广播
- 通过
AndroidManifest
中的<receiver/>
标签注册 - 通过
AMS
的registerReceiver()
接口注册
关于广播使用上的知识大家可以在官网学习:广播基础知识
我们看下BroadcastReceiver
类相关的重要定义:
public abstract class BroadcastReceiver {private PendingResult mPendingResult;public abstract void onReceive(Context context, Intent intent);public final PendingResult goAsync() {PendingResult res = mPendingResult;mPendingResult = null;return res;}
}
实现一个广播接收器只需要继承BroadcastReceiver
,然后实现它的抽象方法onReceive()
就可以了。这也是最简单的广播的用法。
不过从BroadcastReceiver
的类定义中还有一个mPendingResult
变量和goAsync()
方法,它们有什么作用呢?
它们主要是针对当接收到广播后需要做一些耗时操作,而且还有可能需要返回处理结果的情况
- 我们知道
BroadcastReceiver
对象的onReceive()
如果长时间不返回,会引发ANR
- 因此如果要执行耗时的操作,必须在其他线程中完成
mPendingResult
变量的存在就是为了上述需求。而goAsync()
方法提供了获取mPendingResult
变量的接口。
从goAsync()
方法的实现可以看到
- 方法在将
mPendingResult
对象的指针返回后。同时也会将mPendingResult
的值置为null
按照官方说法:
一旦执行了
goAsync()
方法,在onReceive()
结束前,需要将mPendingResult
对象的指针传递到新线程中去,在新线程处理完成后必须调用mPendingResult
对象的finish()
方法来通知AMS
。
我们看下PendingResult
的finish()
方法:
public final void finish() {if (mType == TYPE_COMPONENT) {final IActivityManager mgr = ActivityManager.getService();if (QueuedWork.hasPendingWork()) {QueuedWork.queue(new Runnable() {@Override public void run() {if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,"Finishing broadcast after work to component " + mToken);sendFinished(mgr);}}, false);} else {if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,"Finishing broadcast to component " + mToken);sendFinished(mgr);}} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,"Finishing broadcast to " + mToken);final IActivityManager mgr = ActivityManager.getService();sendFinished(mgr);}}public void sendFinished(IActivityManager am) {synchronized (this) {...try {...if (mOrderedHint) {am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,mAbortBroadcast, mFlags);} else {am.finishReceiver(mToken, 0, null, null, false, mFlags);}} catch (RemoteException ex) {}}}
核心操作就是调用AMS
的finishReceiver()
方法,这才是当前广播接收器的结束的标志。
正常情况下(没有执行过goAsync()
)当onReceive()
调用完成后,AMS
检测到mPendingResult
不为空,便会自动执行finish()
方法,而对于执行过goAsync()
的广播接收器,AMS
便不会主动执行finish()
了
这个
finish()
方法很重要,本章最后也有它的身影
广播的分类
从广播的发送方式来划分,有4类广播。
- 普通广播:通过
Context
中的方法sendBroadcast()
和sendBroadcastAsUser()
发送的广播属于普通广播。- 普通广播的特点是发送给系统当前的所有注册的接收者
- 接收者接收到广播的顺序也是不确定的
- 有序广播:通过
Context
中的方法sendOrderedBroadcast()
和sendOrderedBroadcastAsUser()
发送的广播属于有序广播。- 有序广播的发送顺序是按照接收者的优先级来决定的,系统默认范围是
-1000
-1000
,但实际上没有明确的数值限定,可以到int
最大值 - 接收器的优先级通过匹配的
intent-filter
的android:priority
属性来控制 - 具有相同优先级的接收器将按随机顺序运行(这部分书中和官网的描述并不一致,看完源码再说啦)
- 当接收器逐个顺序执行时,接收器可以向下传递结果,也可以完全中止广播,使其不再传递给其他接收器
- 有序广播的发送顺序是按照接收者的优先级来决定的,系统默认范围是
- 粘性广播:通过
Context
中的方法sendStickyBroadcast()
和sendOrderedBroadcastAsUser()
发送的广播属于粘性广播。- 和前两个广播不同,粘性广播能将广播发送给系统中以后注册的接收者,甚至是新安装应用中的接收者
- 官网解释出于安全因素,在
Android 9
上,这两个接口被标记为了Deprecated
- 不过这并不影响它在
Framework
中的使用,我们看个官网的例子:
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);// 对于一个粘性广播来说,注册监听时便可以得到当前电池状态的 IntentIntent batteryStatus = context.registerReceiver(null, ifilter);
- 本地广播:通过
LocalBroadcastManager.sendBroadcast
发送的广播属于本地广播- 本地广播会将广播发送给与发送者位于同一应用中的接收者
- 当不需要跨应用发送广播时,建议使用本地广播
- 这种实现方法的效率更高(无需进行进程间通信)
- 而且无需担心其他应用在收发广播时带来的任何安全问题
广播的数据结构
在AMS
中,所有注册的广播接收者都放在成员变量mRegisteredReceivers
中,定义如下:
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
mRegisteredReceivers
是一个HashMap
,其中
key
为接收者的IBinder
对象value
为接收者的对象ReceiverList
- 因为一个接收者中可能包含多个
IntentFilter
,所以ReceiverList
是一个数组
final class ReceiverList extends ArrayList<BroadcastFilter>
- 而
BroadcastFilter
则是继承了IntentFilter
,定义如下:
final class BroadcastFilter extends IntentFilter {final ReceiverList receiverList; // 所属 receiver 的引用final String packageName; // 所在应用的包名final String requiredPermission; // 发送广播时需要声明的权限字符串final int owningUid; // 所在应用的 uidfinal int owningUserId; // 所在应用的 userid... }
- 因为一个接收者中可能包含多个
ReceiverList
的关键定义如下:
final class ReceiverList extends ArrayList<BroadcastFilter>implements IBinder.DeathRecipient {final ActivityManagerService owner;public final IIntentReceiver receiver; // 用户进程中定义的 IntentReceiverpublic final ProcessRecord app; // 所属用户进程的 ProcessRecord 对象public final int pid; // 所属用户进程的pidpublic final int uid; // 所属用户进程的uidpublic final int userId; // 用户idBroadcastRecord curBroadcast = null; //boolean linkedToDeath = false; // 是否注册了 binder 死亡监听
}
发送广播时,AMS
中收到的广播消息首先会保存在mBroadcastQueues
对象中,然后再发送给用户进程中的接收者。mBroadcastQueues
是一个只有两个元素的数组,定义如下:
final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];
此外,AMS
中还定义了两个变量:
BroadcastQueue mFgBroadcastQueue;BroadcastQueue mBgBroadcastQueue;
AMS
在构造方法中将这两个变量和mBroadcastQueues
集合进行了关联:
public ActivityManagerService(Context systemContext) {...mFgBroadcastQueue = new BroadcastQueue(this, mHandler,"foreground", BROADCAST_FG_TIMEOUT, false);mBgBroadcastQueue = new BroadcastQueue(this, mHandler,"background", BROADCAST_BG_TIMEOUT, true);mBroadcastQueues[0] = mFgBroadcastQueue;mBroadcastQueues[1] = mBgBroadcastQueue;...}
mFgBroadcastQueue
用来保存带有FLAG_RECEIVER_FOREGROUND
标志的广播,它要求接收者进程以foreground
的优先级运行,这样执行的更快。- 如果不特别指定,一般的广播都会保存在
mBgBroadcastQueue
中
我们看下BroadcastQueue
类的主要结构:
public final class BroadcastQueue {final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
}
mParallelBroadcasts
用来保存所有的普通广播mOrderedBroadcasts
用来保存所有的有序广播
粘性广播在哪里呢?
系统中所有的粘性广播都保存在AMS
的成员变量mStickyBroadcasts
中,定义如下:
final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
mStickyBroadcasts
是一个SparseArray
类型的数组,使用用户Id
作为索引,保存的是ArrayMap
对象ArrayMap
对象储存的是某个用户发送的所有的粘性广播,每条记录以Intent
的action
字符串作为索引,保存的内容是一个ArrayList
对象ArrayList
对象其中储存了包含该action
的Intent
广播的注册过程
动态注册是通过接口registerReceiver()
完成的。应用注册receiver
时,并不是直接调用AMS
的接口来完成的,而是通过Context
类中的方法,因为AMS
的接口需要提供应用的ApplicationThread
类的IBinder
对象来作为参数,应用中得到这个对象比较麻烦。
Context
中的registerReceiver()
最终调用的是ContextImpl
的registerReceiverInternal()
方法,代码如下:
class ContextImpl{private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context, int flags) {...try {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();}}
}
registerReceiverInternal()
方法最终调用的是AMS
的registerReceiver()
方法,代码如下:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver, IntentFilter filter, String permission, int userId,int flags) {// 确保不是 isolated 进程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) {...// 从注册信息中获取 action 信息Iterator<String> actions = filter.actionsIterator();if (actions == null) {ArrayList<String> noAction = new ArrayList<String>(1);noAction.add(null);actions = noAction.iterator();}// 获取 user IDint[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };// 循环遍历 actionwhile (actions.hasNext()) {String action = actions.next();// 遍历每个userID下的粘性广播Mapfor (int id : userIds) {// 获取特定userID下的粘性广播MapArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);if (stickies != null) {// 从Map中查找集合中符合当前action信息的Intent集合ArrayList<Intent> intents = stickies.get(action);if (intents != null) {if (stickyIntents == null) {stickyIntents = new ArrayList<Intent>();}// 添加到 stickyIntents 集合中stickyIntents.addAll(intents);}}}}}ArrayList<Intent> allSticky = null;if (stickyIntents != null) {// 粘性广播不为空final ContentResolver resolver = mContext.getContentResolver();// 查找所有符合符合当前请求的Intentfor (int i = 0, N = stickyIntents.size(); i < N; i++) {Intent intent = stickyIntents.get(i);...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;// 如果注册时传入的 receiver 为空// 此处直接返回 sticky 对象if (receiver == null) {return sticky;}synchronized (this) {if (callerApp != null && (callerApp.thread == null|| callerApp.thread.asBinder() != caller.asBinder())) {// Original caller already diedreturn null;}// 检查注册的 receiver 是否已经注册过了ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());if (rl == null) {// rl为空说明没有注册过// 新建对象rl = new ReceiverList(this, callerApp, callingPid, callingUid,userId, receiver);if (rl.app != null) {// 如果指定了进程对象,则把receiver保存在进程对象中// 方便进程销毁时能及时释放该对象final int totalReceiversForApp = rl.app.receivers.size();if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {throw new IllegalStateException(...);}rl.app.receivers.add(rl);} else {// 如果没有指定进程对象,则注册receiver的死亡通知try {receiver.asBinder().linkToDeath(rl, 0);} catch (RemoteException e) {return sticky;}rl.linkedToDeath = true;}mRegisteredReceivers.put(receiver.asBinder(), rl);// 保存新建的 rl}...// 下面主要是针对 receiver 重复注册的情况BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId, instantApp, visibleToInstantApps);if (rl.containsFilter(filter)) {// 已经包含了本次提交filter,简单打印,不做其他处理Slog.w(TAG, "Receiver with filter " + filter+ " already registered for pid " + rl.pid+ ", callerPackage is " + callerPackage);} else {// 未包含本次提交的filter,添加到集合中rl.add(bf);if (!bf.debugCheck()) {Slog.w(TAG, "==> For Dynamic broadcast");}mReceiverResolver.addFilter(bf);}// 继续处理粘性广播部分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 中的标志获取发送队列(bg还是fg)BroadcastQueue queue = broadcastQueueForIntent(intent);// 创建新的广播对象BroadcastRecord r = new BroadcastRecord(queue, intent, null,null, -1, -1, false, null, null, OP_NONE, null, receivers,null, 0, null, null, false, true, true, -1);// 将广播加入队列queue.enqueueParallelBroadcastLocked(r);// 发送 Intent 到指定进程queue.scheduleBroadcastsLocked();}}return sticky;}}
registerReceiver()
方法看上去比较复杂,其实很多都是针对粘性(sticky
)广播的:
如果没有处理粘性(
sticky
)广播,只需要检查receiver
是否已经注册- 没有注册过,就可以创建用来保存
receiver
的对象ReceiverList
,并把它添加到mRegisteredReceivers
集合中 - 注册过的
receiver
可以重复注册,只要pid、``uid
和userid
相同就可以- 重复注册用于向某个
receiver
增加新的IntentFilter
- 重复注册用于向某个
- 没有注册过,就可以创建用来保存
而对于粘性(
sticky
)广播- 如果调用
registerReceiver()
时参数receiver
为null
,则立刻返回找到的第一个粘性广播的Intent
- 否则,将查找系统中所有和注册时传入的
Intent
匹配的粘性广播,并通过BroadcastQueue
来发送
- 如果调用
我们再来看下广播的发送过程
广播的发送过程
应用发送广播调用的是Context
类中的方法,发送广播的方法虽然很多,最后都是调用AMS
的broadcastIntent()
方法。
broadcastIntent()
方法简单检查了调用者的权限后,转调内部方法broadcastIntentLocked()
来完成广播发送。
broadcastIntentLocked()
方法会
先检查要广播的
Intent
- 如果是一些系统
Intent
,则调用相应的方法处理 - 如果是粘性广播,则把广播的
Intent
加入AMS
的粘性广播列表中
- 如果是一些系统
最后查找所有的接收者,逐个调用它们
广播发送的时序图如下:
AMS.broadcastIntentLocked
我们看下具体实现:
final int broadcastIntentLocked(...) {intent = new Intent(intent);...userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,ALLOW_NON_FULL, "broadcast", callerPackage);// 检查发送广播的用户是否正在运行if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) {if ((callingUid != SYSTEM_UID|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {return ActivityManager.BROADCAST_FAILED_USER_STOPPED;}}final String action = intent.getAction();...// 检查广播是否为 protectedfinal boolean isProtectedBroadcast;try {isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);} catch (RemoteException e) {Slog.w(TAG, "Remote exception", e);return ActivityManager.BROADCAST_SUCCESS;}// 确认发送广播的进程是否为 systemfinal boolean isCallerSystem;switch (UserHandle.getAppId(callingUid)) {case ROOT_UID:case SYSTEM_UID:case PHONE_UID:case BLUETOOTH_UID:case NFC_UID:case SE_UID:isCallerSystem = true;break;default:isCallerSystem = (callerApp != null) && callerApp.persistent;break;}if (!isCallerSystem) {if (isProtectedBroadcast) {// 不是system进程,但是发送的广播却是 protected 的情况// 抛出安全异常...throw new SecurityException(msg);} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {// 不是 system 进程,也不是 protected 广播// 但是,有一些特殊的广播还是不能发送,这里做一些限制if (callerPackage == null) {...throw new SecurityException(msg);} else if (intent.getComponent() != null) {if (!intent.getComponent().getPackageName().equals(callerPackage)) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " to "+ intent.getComponent().getPackageName() + " from "+ callerPackage;...throw new SecurityException(msg);}} else {// Limit broadcast to their own package.intent.setPackage(callerPackage);}}}if (action != null) {// 从 SystemConfig 中查询当前 action 是否在 allow-implicit-broadcast 标签中// 就是查查当前 action 是否支持隐式广播if (getBackgroundLaunchBroadcasts().contains(action)) {...// 如果是,增加一个Flag标记// 这个标记就表明这个广播可以作为隐式广播发送intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);}switch (action) {// 检查处理一些特殊的 action// 这部分主要是检查和 PMS 有关的一些操作case Intent.ACTION_UID_REMOVED:case Intent.ACTION_PACKAGE_REMOVED:case Intent.ACTION_PACKAGE_CHANGED:...// 如果这个广播时从 PMS 来的,而且是关于应用删除或应用变化// 需要从 task 中移除所有和该应用管理的 Activity break;...case "com.android.launcher.action.INSTALL_SHORTCUT":// As of O, we no longer support this broadcasts, even for pre-O apps.// Apps should now be using ShortcutManager.pinRequestShortcut().Log.w(TAG, "Broadcast " + action+ " no longer supported. It will not be delivered.");return ActivityManager.BROADCAST_SUCCESS;}...}// 对于粘性广播if (sticky) {// 检查广播发送方是否声明了 android.permission.BROADCAST_STICKY 权限if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,callingPid, callingUid)!= PackageManager.PERMISSION_GRANTED) {...throw new SecurityException(msg);}if (requiredPermissions != null && requiredPermissions.length > 0) {// 发送粘性广播不能指定 permissionreturn ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;}if (intent.getComponent() != null) {// 粘性广播不能指定特定组件名throw new SecurityException("Sticky broadcasts can't target a specific component");}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");}}}}}// 将广播保存到 mStickyBroadcasts 中ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);if (stickies == null) {stickies = new ArrayMap<>();mStickyBroadcasts.put(userId, stickies);}// 添加 Intent 信息列表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))) {// 如果 Intent 已经存在,覆盖list.set(i, new Intent(intent));break;}}if (i >= stickiesCount) {list.add(new Intent(intent));}}int[] users;if (userId == UserHandle.USER_ALL) {// 广播给所有用户的情况,获取所有的用户idusers = mUserController.getStartedUserArray();} else {// 广播给特定用户users = new int[] {userId};}// 计算所有接收该 Intent 的 receiverList receivers = null;List<BroadcastFilter> registeredReceivers = null;if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {// 如果 Intent 中没有指定 FLAG_RECEIVER_REGISTERED_ONLY // 收集静态接收者receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);}if (intent.getComponent() == null) {// 如果没有指定 Component,则查找匹配该 Intent 的所有 receiverif (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {// 针对发送给所有用户的广播,并且发送广播的 user 是 shellfor (int i = 0; i < users.length; i++) {// 遍历 userID ...// 从 mReceiverResolver 中查找,动态注册的广播接收者都在这里List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, users[i]);if (registeredReceivers == null) {registeredReceivers = registeredReceiversForUser;} else if (registeredReceiversForUser != null) {registeredReceivers.addAll(registeredReceiversForUser);}}} else {registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);}}final boolean replacePending =(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;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 对象BroadcastRecord r = new BroadcastRecord(...);...if (!replaced) {queue.enqueueParallelBroadcastLocked(r); // 加入到并行队列中queue.scheduleBroadcastsLocked(); // 发送广播}registeredReceivers = null;NR = 0;}int ir = 0;if (receivers != null) {// 说明存在静态注册的接收者...int NT = receivers != null ? receivers.size() : 0;int it = 0;ResolveInfo curt = null;BroadcastFilter curr = null;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) {// 优先把动态广播接收者插入到静态广播接收者// 同优先级下,动态接受者会被插入到静态接收者后面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 ((receivers != null && receivers.size() > 0) || resultTo != null) {// 获取广播发送队列BroadcastQueue queue = broadcastQueueForIntent(intent);// 创建广播对象BroadcastRecord r = new BroadcastRecord(...);...final BroadcastRecord oldRecord =replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;if (oldRecord != null) {// 如果存在旧的广播,调用 performReceiveLocked 执行广播if (oldRecord.resultTo != null) {final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);try {// 这个方法就会调用到 onReceived()oldQueue.performReceiveLocked(...);} catch (RemoteException e) {...}}} else {queue.enqueueOrderedBroadcastLocked(r); // 将广播加入到order队列queue.scheduleBroadcastsLocked(); // 发送广播}} ...return ActivityManager.BROADCAST_SUCCESS;}
broadcastIntentLocked()
方法比较长,我们再简单梳理下:
- 首先是检查发送的广播是否为一些特殊的广播
- 尤其是从
PMS
中发出的有关应用安装变化相关的广播
- 尤其是从
- 接下来是判断是否为粘性广播,如果是
- 先是检查一些相关的权限,比如粘性广播不能带有
permission
,而且Intent
必须指定Component
- 然后创建对应的数据结构,并添加到
mStickyBroadcasts
集合中
- 先是检查一些相关的权限,比如粘性广播不能带有
- 然后查找该广播对应的广播接收者
- 如果是普通广播,会优先发给动态接收者
- 如果是有序广播,会将动态接收者和静态接收者一起根据优先级排序,然后再创建
BroadcastRecord
对象并添加到有序广播队列中
- 最后,通过
scheduleBroadcastsLocked()
发送广播
从上面的代码实现上可以看到,无论哪种广播,静态接收者之间一定会排序,而且相同优先级下,静态接收者会排在动态接收者之前。
为什么静态接收者一定要放到有序队列呢?
对于静态接收者来说,它所属的进程可能已经在运行,也可能没有。如果进程没有运行,就需要先启动它。首先进程的启动是一个耗时过程,而且启动有可能失败,这个过程只能逐一处理,不能简单的群发消息。
到这里,广播接收者和广播内容都已经检查准备好了,我们继续学习scheduleBroadcastsLocked()
广播发送的过程
BroadcastQueue.scheduleBroadcastsLocked
scheduleBroadcastsLocked()
方法只是发送了一个BROADCAST_INTENT_MSG
消息,代码如下:
public void scheduleBroadcastsLocked() {...mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));mBroadcastsScheduled = true;}
BROADCAST_INTENT_MSG
消息的处理是调用processNextBroadcast()
方法
case BROADCAST_INTENT_MSG: {...processNextBroadcast(true);} break;final void processNextBroadcast(boolean fromMsg) {synchronized (mService) {processNextBroadcastLocked(fromMsg, false);}}
调用到了processNextBroadcastLocked()
方法。这个方法比较长,我们简要分析:
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {BroadcastRecord r;...// 循环处理 mParallelBroadcasts 集合中的消息while (mParallelBroadcasts.size() > 0) {r = mParallelBroadcasts.remove(0);r.dispatchTime = SystemClock.uptimeMillis();r.dispatchClockTime = System.currentTimeMillis();...final int N = r.receivers.size();...for (int i=0; i<N; i++) {Object target = r.receivers.get(i);...// 调用通知 receiverdeliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);}addBroadcastToHistoryLocked(r);}// 接下来处理 mPendingBroadcast 集合// 这个集合存放的是等待进程启动的广播if (mPendingBroadcast != null) {...boolean isDead;if (mPendingBroadcast.curApp.pid > 0) {synchronized (mService.mPidsSelfLocked) {ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);isDead = proc == null || proc.crashing;}} else {final ProcessRecord proc = mService.mProcessNames.get(mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);isDead = proc == null || !proc.pendingStart;}if (!isDead) {// It's still alive, so keep waitingreturn;} else {Slog.w(TAG, "pending app ["+ mQueueName + "]" + mPendingBroadcast.curApp+ " died before responding to broadcast");mPendingBroadcast.state = BroadcastRecord.IDLE;mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;mPendingBroadcast = null;}}// 处理 mOrderedBroadcasts 中的广播boolean looped = false;do {...r = mOrderedBroadcasts.get(0);boolean forceReceive = false;...int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;if (mService.mProcessesReady && r.dispatchTime > 0) {long now = SystemClock.uptimeMillis();// forground 的 mTimeoutPeriod 超时是 10*1000// bgground 的 mTimeoutPeriod 超时是 60*1000if ((numReceivers > 0) && (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {// 如果超时则终止广播...broadcastTimeoutLocked(false); // forcibly finish this broadcastforceReceive = true;r.state = BroadcastRecord.IDLE;}}...if (r.receivers == null || r.nextReceiver >= numReceivers|| r.resultAbort || forceReceive) {// 没有多余的 receiver 了if (r.resultTo != null) {try {...// 把广播结果传递给发送广播的进程performReceiveLocked(r.callerApp, r.resultTo,new Intent(r.intent), r.resultCode,r.resultData, r.resultExtras, false, false, r.userId);r.resultTo = null;} catch (RemoteException e) {r.resultTo = null;...}}...}} while (r == null);...if (nextReceiver instanceof BroadcastFilter) {BroadcastFilter filter = (BroadcastFilter)nextReceiver;...// 发送给接收者进程deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);if (r.receiver == null || !r.ordered) {...// 已经处理完成的情况r.state = BroadcastRecord.IDLE;// 处理下一条广播scheduleBroadcastsLocked();} else {if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {scheduleTempWhitelistLocked(filter.owningUid,brOptions.getTemporaryAppWhitelistDuration(), r);}}return;}...ProcessRecord app = mService.getProcessRecordLocked(targetProcess,info.activityInfo.applicationInfo.uid, false);...if (app != null && app.thread != null && !app.killed) {// 如果是静态接收者,并且接收者对应的进程已经启动的情况try {app.addPackage(info.activityInfo.packageName,info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);// 调用 processCurBroadcastLocked 通知 receiverprocessCurBroadcastLocked(r, app, skipOomAdj);// 直接返回return;} ...}...// 走到这里说明进程没有启动,先调用 startProcessLocked 启动进程if ((r.curApp=mService.startProcessLocked(...)) == null) {....// 如果进程启动失败,处理下一条广播scheduleBroadcastsLocked();r.state = BroadcastRecord.IDLE;return;}mPendingBroadcast = r;mPendingBroadcastRecvIndex = recIdx;
}
processNextBroadcastLocked()
方法也只是对要发送的广播接收者的集合进行的遍历发送处理,真正通知到应用的部分是在deliverToRegisteredReceiverLocked()
方法中,我们具体看下。
BroadcastQueue.deliverToRegisteredReceiverLocked
方法如下:
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,BroadcastFilter filter, boolean ordered, int index) {...performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,new Intent(r.intent), r.resultCode, r.resultData,r.resultExtras, r.ordered, r.initialSticky, r.userId);...}void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, int resultCode, String data, Bundle extras,boolean ordered, boolean sticky, int sendingUser) throws RemoteException {// Send the intent to the receiver asynchronously using one-way binder calls.if (app != null) {if (app.thread != null) {...app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.repProcState);...}} else {receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);}}
我们已经在performReceiveLocked
方法中明显看到app.thread
字样了,这也就意味着开始去调用应用进程中的接口了,也就是ActivityThread
中的scheduleRegisteredReceiver()
接口,如下:
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,int resultCode, String dataStr, Bundle extras, boolean ordered,boolean sticky, int sendingUser, int processState) throws RemoteException {...receiver.performReceive(intent, resultCode, dataStr, extras, ordered,sticky, sendingUser);}
ActivityThread
调用的是IIntentReceiver
的performReceive()
方法
IIntentReceiver
是一个AIDL
,跟踪实现是在LoadedApk
中,具体的装配过程是在ContextImpl
初始化时完成的
我们看下在LoadedApk
中的关键定义
LoadedApk.performReceive
代码如下:
class LoadedApk{static final class ReceiverDispatcher {final static class InnerReceiver extends IIntentReceiver.Stub {...@Overridepublic void performReceive(...) {final LoadedApk.ReceiverDispatcher rd;...// 调用内部的 performReceive() 方法rd.performReceive(...);...}}...public void performReceive(...) {final Args args = new Args(...);...// 此处重点是执行通过 Args.getRunnable() 获取的 Runnable 对象// Runnable 对象中执行了 onReceive() 方法回调if (intent == null || !mActivityThread.post(args.getRunnable())) {if (mRegistered && ordered) {IActivityManager mgr = ActivityManager.getService();...args.sendFinished(mgr);}}}final class Args extends BroadcastReceiver.PendingResult {...public final Runnable getRunnable() {return () -> {final BroadcastReceiver receiver = mReceiver;...try {...// onReceive 回调receiver.onReceive(mContext, intent);}...if (receiver.getPendingResult() != null) {// mPendingResult 不为空,自动执行 finish()finish();}};}}}
}
这样一个广播的发送就结束了。。。
AMS
的章节终于是磕磕绊绊的学完了,很多细节没有掌握,好在整体流程上暂时还留有印象。留此笔记方便复习
下一篇Android的图形显示系统
深入Android系统(十一)AMS-4-广播管理相关推荐
- android系统 PowerManager深入分析(非常详细)
概述 一直以来,电源管理是电子产品设计中非常重要的环节,也是任何电子设备中最为重要的系统模块之一,优秀的电源管理方案,能够提供持久的续航能力,良好的用户体验,更能提升电子产品的竞争力. 移动设备的电量 ...
- Android源码分析之广播的发送和接收流程
说明:本文是基于Android6.0源码来分析的 概要 我的理解是,Android中的广播可以看为一种消息机制,用来在一定的条件下触发一些操纵,比如:网络状态的改变,熄屏,亮屏等等Android系统都 ...
- android手机界面管理系统的设计与实现(硕士学位论文).pdf,基于Android系统的手机文件管理器的设计与实现...
摘要: 在移动终端技术不断发展的今天,智能手机支持的业务也越来越丰富,已经从简单的通话工具发展成为集PDA,互动游戏,高分辨率摄像,移动视听于一体的全功能通讯,数据处理工具,其操作系统平台也从最初的L ...
- Android 系统框架结构
目录 1.应用层(System Apps): 2.应用框架层(Java API Framework): 3.系统运行库层(Native): 4.硬件抽象层(HAL): 5.Linux内核层(Linux ...
- 第十一篇 ANDROID 系统网络连接和管理机制与架构
一 网络连接功能介绍 ANDROID 系统网络连接和管理服务由四个系统服务ConnectivityService.NetworkPolicyManagerService.NetworkManagem ...
- Android插件化原理解析——广播的管理
在Activity生命周期管理 以及 插件加载机制 中我们详细讲述了插件化过程中对于Activity组件的处理方式,为了实现Activity的插件化我们付出了相当多的努力:那么Android系统的其他 ...
- Android 9.0系统源码_广播(一)广播的注册
前言 广播作为四大组件之一,使用频率远没有Activity高,但是广播的工作过程还是十分有必要了解的.本系列文章将会逐步讲述广播的注册.发送和接受:而本篇我们要讲的就是广播的注册. 广播的注册分为两种 ...
- Android广播管理二--广播注册(registerReceiver)流程分析
前面分析了Android系统的广播机制,从本质来说,它是一种消息订阅/发布机制.因此,使用这种消息驱动模型的第一步便是订阅消息:而对Android应用程序来说,订阅消息其实就是注册广播接收器. 接下来 ...
- android系统休眠发广播,Android - BroadcastReceiver
BroadcastReceiver BroadcastReceiver,广播接收者,用来接收系统和应用的广播,并做出相应的处理,如电量过低时提示用户充电等: BroadcastReceiver 是 A ...
- Android系统中的进程管理:进程的创建
对于操作系统来说,进程管理是其最重要的职责之一. 考虑到这部分的内容较多,因此会拆分成几篇文章来讲解. 本文是进程管理系统文章的第一篇,会讲解Android系统中的进程创建. 本文适合Android平 ...
最新文章
- 《lua程序设计(第二版)》学习笔记(五)-- 函数基础
- 【ACM】UVa 1339
- IIS7 上配置运行 Ruby CGI环境
- 9.任务段(TSS)
- 随机迷宫 c语言思路,[原创]递归随机迷宫生成算法详解
- mysql8.0.19.0_分享MySql8.0.19 安装采坑记录
- [蓝桥杯2015初赛]生命之树-求树的最大子树权值和
- Java ASM与Javassit
- 天池 在线编程 安排面试城市(贪心)
- java基础----java调用oracle存储过程(转)
- 【优化分类】基于matlab遗传算法优化支持向量机分类(多输入多分类)【含Matlab源码 QF003期】
- Vmware里Ubuntu安装Vmware Tools时提示:客户机操作系统己将 CD-ROM 门锁定,并且可能正在使用 CD-ROM.....
- 在VB中使用API“SHFileOperation”删除文件的三个问题
- 淘宝网上卖F22飞机,一群强人提问
- 了解云桌面,看这一篇文章就够了
- 气象ts评分_天气预报评分方法评述.doc
- selenium的webdrive驱动安装(谷歌浏览器)
- 2020年北大软微推免经历
- 中国大学计算机系写英语论文,计算机专业英语学论文题目 计算机专业英语论文题目怎样取...
- 犯了这5个UI设计错误你的APP就得失败啦
热门文章
- arcpy中拆分获取FeatureClass中各类型地物要素到单独的shp中,类似于splitShp的功能(地理国情监测)
- 考研计算机网络专业术语解释,计算机:考研统考大纲权威解读之计算机网络
- net net net
- 【CAD软件常见问题】中望CAD方面介绍和教程
- 2021绥化高考成绩查询,绥化中考成绩查询2021
- 大生态里的小生态:IBOS开源OA借道云市场构建高价值协同云
- 录音软件哪个好?当然是最专业最好用的。
- xp计算机时间与网络时间不一致,XP系统时间同步失败(无法启动Windows时间服务)的详细说明...
- 猫狗大战2014,阿里京东预谋了一年的明争暗斗
- 垃圾邮件是如何用贝叶斯方法过滤掉的