Android 广播发送流程分析

在上一篇文章中Android 广播阻塞、延迟问题分析方法讲了广播阻塞的分析方法,但是分析完这个问题,自己还是有一些疑问:

  • 广播为啥会阻塞呢?发送给接收器就行了,为啥还要等着接收器处理完才处理下一个?
  • 由普通的后台广播改为前台广播后,为啥处理的会更快?

这篇参考文章Android 9.0系统源码_广播(三)广播的发送来学习下广播的分发过程,这样对广播阻塞这个问题会理解更深刻。

首先,复习下广播相关的概念。

一、广播相关概念

广播分类

1、标准广播:异步广播,广播发出后,所有注册了的广播接收器都会同时接收到该广播。打个比方:做地铁过程中的语音播报,当列车员(广播发出者)进行语音播报(发送广播)时,所有乘客(注册接收该广播的程序)都可以同时听到语音,不分先后顺序。

sendBroadcast(); 

2、有序广播:同步发送,广播发出后,按照注册了的广播接收器的优先级顺序广播,优先级的范围是-1000~1000,数字越大,优先级越高,最先接收到该广播,接收器的优先级通过android:priority设置,并且先收到广播的可以修改或者抛弃该广播,后面优先级小的接收器就无法收到了。同级别的广播,动态优先于静态;同级别同类型的广播,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。

sendOrderedBroadcast();

3、粘性广播:粘性广播的机制就是发送广播的时候如果没有找到接收器,就会先保存下来,等有广播接收器注册的时候,再把之前保存的广播发出去。因为从Android 5.0(API 21)开始,因为安全性的问题,官方已经正式废弃了粘性广播。

sendStickyBroadcast();

广播注册方式

1、动态注册:在Context(即Service或Activity)组件中注册,通过registerReceiver()方法注册,在不使用时取消注册unregisterReceiver()。如果广播发送的时候注册Broadcast的组件没有启动的话,是收不到广播的。

2、 静态注册:在AndroidManifest.xml中注册,并在intent-filter中添加响应的action,并新建一个Broadcast组件类来处理注册广播。如果广播发送的时候Broadcast的组件类进程没有启动的话,会收到广播并启动进程。

具体实现可参考Android的有序广播和无序广播(解决安卓8.0版本之后有序广播的接收问题) 区分了Android8.0前后的实现方式。

前台广播及后台广播

前台广播:在广播发送的时候添加Intent.FLAG_RECEIVER_FOREGROUND flag。
后台广播:在广播发送的时候没有Intent.FLAG_RECEIVER_FOREGROUND flag。

默认情况下,Intent是不带Intent.FLAG_RECEIVER_FOREGROUND 的flag的,所以我们默认使用的都是后台广播。

二、广播发送流程

参考文章写得非常详细,建议大家好好看看,我是看完又按照自己的思路整理了一遍写个笔记,只是大体的逻辑思路,重点是帮助我搞清楚上面对广播的一些疑问。

1、调用ContextImpl的sendBroadcast发送广播

frameworks/base/core/java/android/app/ContextImpl.java

@Override
public void sendBroadcast(Intent intent) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess(this);//直接调用AMS的broadcastIntent方法ActivityManager.getService().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null,Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,getUserId());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

2、调用AMS的broadcastIntent方法

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

// 发送广播
public final int broadcastIntent(IApplicationThread caller,Intent intent, String resolvedType, IIntentReceiver resultTo,int resultCode, String resultData, Bundle resultExtras,String[] requiredPermissions, int appOp, Bundle bOptions,boolean serialized, boolean sticky, int userId) {enforceNotIsolatedCaller("broadcastIntent");synchronized (this) {//验证广播的Intent是否合法intent = verifyBroadcastLocked(intent);// 根据caller从缓存mLruProcesses中获取进程对象ProcessRecordfinal ProcessRecord callerApp = getRecordForAppLocked(caller);final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();int res = broadcastIntentLocked(callerApp,callerApp != null ? callerApp.info.packageName : null,intent, resolvedType, resultTo, resultCode, resultData, resultExtras,requiredPermissions, appOp, bOptions, serialized, sticky,callingPid, callingUid, userId);Binder.restoreCallingIdentity(origId);return res;}
}

3、调用AMS的broadcastIntentLocked方法

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

这个方法非常长,拆开分别来看:

3.1 系统广播处理broadcastIntentLocked

final int broadcastIntentLocked(ProcessRecord callerApp,String callerPackage, Intent intent, String resolvedType,IIntentReceiver resultTo, int resultCode, String resultData,Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {intent = new Intent(intent);final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);// Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPSif (callerInstantApp) {intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);}// By default broadcasts do not go to stopped apps.// 增加下面flag,默认不发送广播到已经停止的appintent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);// If we have not finished booting, don't allow this to launch new processes.// 如果该进程还没有完成启动,并且不是发送给启动升级的广播,则添只发送给已注册的广播接收者标签if (!mProcessesReady && (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);// 只发给注册的receiver}// 获取当前发送广播应用所在用户的userIduserId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,ALLOW_NON_FULL, "broadcast", callerPackage);// Make sure that the user who is receiving this broadcast or its parent is running.// If not, we will just skip it. Make an exception for shutdown broadcasts, upgrade steps.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();BroadcastOptions brOptions = null;...// 验证是不是受保护的广播(是不是系统广播)final boolean isProtectedBroadcast;try {           isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);} catch (RemoteException e) {return ActivityManager.BROADCAST_SUCCESS;}// 检查是不是系统调用final boolean isCallerSystem;switch (UserHandle.getAppId(callingUid)) {case Process.ROOT_UID:case Process.SYSTEM_UID:case Process.PHONE_UID:case Process.BLUETOOTH_UID:case Process.NFC_UID:isCallerSystem = true;break;default:isCallerSystem = (callerApp != null) && callerApp.persistent;break;}// First line security check before anything else: stop non-system apps from// sending protected broadcasts.if (!isCallerSystem) {// 不是系统发送的广播if (isProtectedBroadcast) {// 非系统进程发送受保护广播抛出异常throw new SecurityException(msg);} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {// 如果是APPWIDGET的配置和升级的广播// Special case for compatibility: we don't want apps to send this,// but historically it has not been protected and apps may be using it// to poke their own app widget.  So, instead of making it protected,// just limit it to the caller.if (callerPackage == null) {throw new SecurityException(msg);} else if (intent.getComponent() != null) {// They are good enough to send to an explicit component...  verify// it is being sent to the calling app.if (!intent.getComponent().getPackageName().equals(callerPackage)) {throw new SecurityException(msg);}} else {// Limit broadcast to their own package.// 限制发送广播给自己包里intent.setPackage(callerPackage);}}}// 下面主要是针对系统广播的处理if (action != null) {switch (action) {case Intent.ACTION_UID_REMOVED:// 移除uidcase Intent.ACTION_PACKAGE_REMOVED:// 卸载应用case Intent.ACTION_PACKAGE_CHANGED:// 应用更改,比如:停用,启动等case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// 外部应用不可用,比如安装到sd卡的应用,卸载了sd卡case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:// 外部应用可用case Intent.ACTION_PACKAGES_SUSPENDED:// 暂停应用case Intent.ACTION_PACKAGES_UNSUSPENDED:// 应用可用switch (action) {case Intent.ACTION_UID_REMOVED:// 移除系统userId(删除一个用户)...break;case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// 外部应用不可用,一般是卸载SD...break;case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:// 外部应用可用,一般是插入SD...break;case Intent.ACTION_PACKAGE_REMOVED:// 卸载case Intent.ACTION_PACKAGE_CHANGED:// 更新...break;case Intent.ACTION_PACKAGES_SUSPENDED:case Intent.ACTION_PACKAGES_UNSUSPENDED:...break;}break;case Intent.ACTION_PACKAGE_REPLACED: {// 升级应用...break;}case Intent.ACTION_PACKAGE_ADDED: {// 安装应用...break;}case Intent.ACTION_PACKAGE_DATA_CLEARED: {// 清理应用数据...break;}case Intent.ACTION_TIMEZONE_CHANGED:// 时区改变...break;case Intent.ACTION_TIME_CHANGED:// 时间改变...break;case Intent.ACTION_CLEAR_DNS_CACHE:// 清理DNS缓存...break;case Proxy.PROXY_CHANGE_ACTION:// 代理改变...break;case android.hardware.Camera.ACTION_NEW_PICTURE:// 新照片case android.hardware.Camera.ACTION_NEW_VIDEO:// 新视频...break:case android.security.KeyChain.ACTION_TRUST_STORE_CHANGED:...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().return ActivityManager.BROADCAST_SUCCESS;}}...
}

上面这个部分主要干了三件事:

  • 一些变量值得获取,例如:userIdaction等;
  • 一些特殊情况、异常判断的处理;
  • 对系统广播的判断和处理。

3.2 粘性广播处理

       // Add to the sticky list if requested.// 判断是否是粘性广播,如果是,AMS就需要保存这个广播,以便后面注册要接收此类型广播的接收者可以获得这个广播if (sticky) {// 检查粘性广播是否申请了权限if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, callingPid, callingUid)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException(msg);}if (requiredPermissions != null && requiredPermissions.length > 0) {return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;}// sticky广播不能指定目标组件if (intent.getComponent() != null) {throw new SecurityException("Sticky broadcasts can't target a specific component");}// We use userId directly here, since the "all" target is maintained// as a separate set of sticky broadcasts.//不是发送给所有用户的广播if (userId != UserHandle.USER_ALL) {// But first, if this is not a broadcast to all users, then// make sure it doesn't conflict with an existing broadcast to// all users.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);// 该广播列表中没有该用户的stick广播列表if (stickies == null) {stickies = new ArrayMap<>();mStickyBroadcasts.put(userId, stickies);}// 获取注册广播的Action对应的粘性广播的Intent列表ArrayList<Intent> list = stickies.get(intent.getAction());if (list == null) {//如果为空list = new ArrayList<>();//创建一个列表stickies.put(intent.getAction(), list);//以action为键保存该列表}// 获取该action对应粘性广播Intent列表的个数final int stickiesCount = list.size();int i;// 检查在粘性广播列表中是否保存了一个与参数Intent一致的广播。如果存在直接替换,否则将参数// Intent描述的广播添加到粘性广播列表list中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) {// 如果该列表中不存在该粘性广播的Intent加入进去list.add(new Intent(intent));   }}

上面这部分是对粘性广播的处理过程。把粘性广播放在了list列表中,而listaction为键放置在了stickies中,而stickies又以userId为键放在了mStickyBroadcasts中,因此mStickyBroadcasts保存了设备中所有用户粘性广播的Intent相关信息。

3.3 获取静态和动态注册的接收器

		int[] users;if (userId == UserHandle.USER_ALL) {// Caller wants broadcast to go to all started users.// 获取所有已启动用户的列表users = mUserController.getStartedUserArrayLocked();} else {// 发送广播给指定用户// Caller wants broadcast to go to one specific user.users = new int[]{userId};}// Figure out who all will receive this broadcast.List receivers = null;//静态注册接收者List<BroadcastFilter> registeredReceivers = null;//动态注册接收者// Need to resolve the intent to interested receivers...// 如果当前的广播Intent没有指定FLAG_RECEIVER_REGISTERED_ONLY标记,也就是允许静态注册   if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {// 允许静态注册的Intent,需要从PMS中去查询对应的广播接收者receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);}// 如果参数intent没有指定广播接收者的组件名,说明是发送给所有已注册并且要接收该广播的接收者的if (intent.getComponent() == null) {if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {// Query one target user at a time, excluding shell-restricted users// 查找所有用户的所有动态注册的广播接收者for (int i = 0; i < users.length; i++) {if (mUserController.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {continue;}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);}}
  • 上面这段代码主要是获取静态和动态注册的接收器。其中receivers 表示静态注册接收器列表, registeredReceivers 表示动态注册接收器列表;
  • Intent.FLAG_RECEIVER_REGISTERED_ONLYflag表示仅支持动态注册,不支持静态注册,如果在manifest中注册是收不到该广播的;
  • 如果发送广播不加Component信息会遍历获取所有的当前intent的接收者,因此如果定向发给某个应用的话,要把Component信息加上。

3.4 将无序动态注册接收器添加到并发列表中

		//是否支持替换之前待发出的广播final boolean replacePending = (intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;// 动态注册的广播接收者个数int NR = registeredReceivers != null ? registeredReceivers.size() : 0;// 当前发送的是无序广播,且存在动态注册的广播接收者if (!ordered && NR > 0) {// 如果当前发送的广播是无序的,后续是通过并发的方式发送给对应的动态注册的广播接收者的,不需要等待// If we are not serializing this broadcast, then send the// registered receivers separately so they don't wait for the// components to be launched.//如果发送方是系统if (isCallerSystem) {checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,isProtectedBroadcast, registeredReceivers);}// 根据intent查找对应的广播队列,前台或者后台队列final BroadcastQueue queue = broadcastQueueForIntent(intent);// 创建BroadcastRecord对象并将当前所有通过动态注册的广播接收者传进去BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,resultCode, resultData, resultExtras, ordered, sticky, false, userId);// 在BroadcastQueue中等待发送广播中搜索是否有相同的BroadcastRecord并且是否替换final boolean replaced = replacePending && (queue.replaceParallelBroadcastLocked(r) != null);// Note: We assume resultTo is null for non-ordered broadcasts.if (!replaced) {// 如果不需要替换则插入到BroadcastQueue中// 也就是说动态广播接收者都放在了BroadcastQueue的mParallelBroadcasts中queue.enqueueParallelBroadcastLocked(r);//并推动一次广播发送queue.scheduleBroadcastsLocked();}registeredReceivers = null;NR = 0;}
  • 上面这段代码是,如果发送的是无序广播,且存在动态注册的广播接收者,就将该广播加入并行处理队列,并进行一次广播发送。简单来说就是【动态注册且接受无序广播的广播接收者】是并行操作,广播发送速度会比较快。
  • Intent.FLAG_RECEIVER_REPLACE_PENDINGflag表示是否替换待发出的广播,如果flag为1会进行替换, 位置与待发广播一样。
  • 代码中出现关于队列相关的逻辑后面讲,下面来看下【动态注册接受有序广播的广播接收者】和【静态注册的广播接收者】的广播发送。

3.5 合并有序动态注册和静态注册接收器

		 // Merge into one list.int ir = 0;if (receivers != null) {// 对于ACTION_PACKAGE_ADDED广播而言,如果是自己被add了,那么这个广播只能别人收到,// 自己即使注册了这个静态广播也接收不到,注释上说是担心有些应用一安装就接收自己的PACKAGE_ADDED// 广播,然后就启动了。简言之,应用永远接收不到自己的PACKAGE_ADDED广播。// A special case for PACKAGE_ADDED: do not allow the package// being added to see this broadcast.  This prevents them from// using this as a back door to get run as soon as they are// installed.  Maybe in the future we want to have a special install// broadcast or such for apps, but we'd like to deliberately make// this decision.String skipPackages[] = null;// 需要跳过的广播if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())|| Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())|| Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {Uri data = intent.getData();if (data != null) {String pkgName = data.getSchemeSpecificPart();if (pkgName != null) {skipPackages = new String[]{pkgName};}}} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);}if (skipPackages != null && (skipPackages.length > 0)) {for (String skipPackage : skipPackages) {if (skipPackage != null) {int NT = receivers.size();for (int it = 0; it < NT; it++) {ResolveInfo curt = (ResolveInfo) receivers.get(it);if (curt.activityInfo.packageName.equals(skipPackage)) {receivers.remove(it);it--;NT--;}}}}}// 开始把动态注册的广播接收者列表registeredReceivers和静态注册的广播接收者列表receivers合并成一个列表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);//获取静态广播接收者列表第it个条目}if (curr == null) {curr = registeredReceivers.get(ir);//获取动态广播接收者列表第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;}}}// 将两个列表合并到一个列表中,这样静态注册广播接收者receivers列表就包含了所有的广播接收者(无序广播和有序广播)。while (ir < NR) {if (receivers == null) {receivers = new ArrayList();}receivers.add(registeredReceivers.get(ir));ir++;}if (isCallerSystem) {checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,isProtectedBroadcast, receivers);}
  • 上面这段代码主要是对【动态注册接受有序广播的广播接收者】和【静态注册的广播接收者】进行合并,如果是有序广播,动态接收者和静态的接收者合并到一个队列里面进行处理,也就是说order广播下,所有的接收者(静态和动态)处理方式都是一样的,都是串行处理;
  • 对于静态注册的接收者而言,始终是和order广播的处理方式是一样的,也就是说静态的接收者只有order模式(串行化接收);
  • 在合并过程中,如果一个动态注册的广播接收者和一个静态注册的目标广播接收者的优先级相同,那么动态注册的目标接收者会排在静态注册的目标广播接收者前面,即动态注册的目标广播接收者会优先于静态注册的广播接收者接收到有序广播。

3.6 合并接收器添加到有序处理列表中

 		// 可以看出,是在合并入receiver后统一发送BroadcastQueue.scheduleBroadcastsLockedif ((receivers != null && receivers.size() > 0) || resultTo != null) {// 根据intent查找对应的广播队列final BroadcastQueue queue = broadcastQueueForIntent(intent);// 创建BroadcastRecord对象并将合并过的广播接收者列表receivers传进去BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,resultData, resultExtras, ordered, sticky, false, userId);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r+ ": prev had " + queue.mOrderedBroadcasts.size());if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,"Enqueueing broadcast " + r.intent.getAction());final BroadcastRecord oldRecord = replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;if (oldRecord != null) {// Replaced, fire the result-to receiver.if (oldRecord.resultTo != null) {final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);try {oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,oldRecord.intent,Activity.RESULT_CANCELED, null, null,false, false, oldRecord.userId);} catch (RemoteException e) {Slog.w(TAG, "Failure ["+ queue.mQueueName + "] sending broadcast result of "+ intent, e);}}} else {//无序广播+有序广播合并之后放在了BroadcastQueue的mOrderedBroadcasts中queue.enqueueOrderedBroadcastLocked(r);//并推动一次广播发送queue.scheduleBroadcastsLocked();}} else {// There was nobody interested in the broadcast, but we still want to record// that it happened.if (intent.getComponent() == null && intent.getPackage() == null&& (intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {// This was an implicit broadcast... let's record it for posterity.addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);}}

上面这段代码是将合并后的接收添加到有序队列中进行处理。

系统广播处理broadcastIntentLocked函数这部分代码逻辑总结:

  • 首先是判断是不是系统广播,也就是switch语句中的部分,这部分的广播是系统发出的,根据不同广播做出不同的处理,系统广播我们可以接收但是不能发送,只能由系统发出;
  • 接着是粘性广播的处理;
  • 然后是【动态注册且接受无序广播的广播接收者】的处理,把他们放入到BroadcastQueue的mParallelBroadcasts中,并调用scheduleBroadcastsLocked,BroadcastQueue对mParallelBroadcasts列表中条目的最终处理是通过并行操作来完成的;
  • 最后是【动态注册接受有序广播的广播接收者】和【静态注册的广播接收者】的处理,把他们放入到BroadcastQueue的mOrderedBroadcasts中,并调用scheduleBroadcastsLocked,BroadcastQueue对mOrderedBroadcasts列表中条目的最终处理是通过串行操作来完成的;

4、BroadcastQueue广播处理

4.1 BroadcastQueue队列定义及入队列方法判断

frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

/*** Lists of all active broadcasts that are to be executed immediately* (without waiting for another broadcast to finish).  Currently this only* contains broadcasts to registered receivers, to avoid spinning up* a bunch of processes to execute IntentReceiver components.  Background-* and foreground-priority broadcasts are queued separately.*/
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();/*** List of all active broadcasts that are to be executed one at a time.* The object at the top of the list is the currently activity broadcasts;* those after it are waiting for the top to finish.  As with parallel* broadcasts, separate background- and foreground-priority queues are* maintained.*/
final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();

在广播队列中定义了两个处理列表,一个是并发处理列表mParallelBroadcasts,一个是有序处理列表mOrderedBroadcasts

BroadcastQueue broadcastQueueForIntent(Intent intent) {final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,"Broadcast intent " + intent + " on "+ (isFg ? "foreground" : "background") + " queue");return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}public void enqueueParallelBroadcastLocked(BroadcastRecord r) {mParallelBroadcasts.add(r);r.enqueueClockTime = System.currentTimeMillis();
}public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {mOrderedBroadcasts.add(r);r.enqueueClockTime = System.currentTimeMillis();
} 

上面的三个方法是上面源码中出现的,broadcastQueueForIntent根据intent是否含有Intent.FLAG_RECEIVER_FOREGROUND来判断是前台广播还是普通后台广播,如果是前台广播返回前台广播处队列。如果是普通后台广播返回后台广播处理队列

获取队列后,将发给【动态注册且接受无序广播的广播接收者】的广播通过enqueueParallelBroadcastLocked函数添加到该队列的并发处理列表中。将发给【动态注册接受有序广播的广播接收者】和【静态注册的广播接收者】的广播通过enqueueOrderedBroadcastLocked函数添加到该队列的有序处理列表中。

4.2 BroadcastQueue队列中广播发送流程

从上面的源码分析中看到最后我们都执行了scheduleBroadcastsLocked对广播进行了进一步发送处理,接下来看下这个函数的逻辑。

// 执行广播发送,所有广播都会从这里走,然后会到processNextBroadcast
public void scheduleBroadcastsLocked() {// 来调度保存在无序广播调度队列mParallelBroadcasts和有序广播调度队列mOrderedBroadcasts中的广播转发任务的if (mBroadcastsScheduled) {return;}// 虽然这里只发送了发送广播的消息,但是这一步执行完之后就已经标记广播发送了,因此可以看出广播发送和处理// 是异步的,即广播发送者将一个广播发送给AMS后,不会等待AMS将这个广播转发给广播接收者处理mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));mBroadcastsScheduled = true;
}//BroadcastQueue中的消息调度BroadcastHandler 
private final class BroadcastHandler extends Handler {public BroadcastHandler(Looper looper) {super(looper, null, true);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case BROADCAST_INTENT_MSG: {processNextBroadcast(true);//开始处理下一个广播}break;case BROADCAST_TIMEOUT_MSG: {synchronized (mService) {broadcastTimeoutLocked(true);}}break;}}
}
  • 先判断mBroadcastsScheduled的值,如果true说明消息队列已经存在一个类型为BROADCAST_INTENT_MSG的消息了,直接返回,因为该消息在执行完毕会自动调用下一条消息;
  • 接下来广播的处理逻辑会走到 processNextBroadcast函数中,下面来看下该函数的逻辑。

4.2.1 BroadcastQueue并发列表广播发送流程

//参数fromMsg是用来描述processNextBroadcast是否是被handleMessage触发的
final void processNextBroadcast(boolean fromMsg) {synchronized (mService) {processNextBroadcastLocked(fromMsg, false);}
}
// 广播发送的核心内容
final void processNextBroadcastLocked(boolean fromMsg) {BroadcastRecord r;mService.updateCpuStats();// 否是来自handleMessage的BROADCAST_INTENT_MSG类型消息if (fromMsg) {// 前面说到,如果消息队列里面有BROADCAST_INTENT_MSG消息,该标记为true,阻止新的消息加入队列,// 这里开始处理这个消息的时候,将mBroadcastsScheduled变量设置为false,开始允许新的消息加入。mBroadcastsScheduled = false;}// First, deliver any non-serialized broadcasts right away.// 无序广播之间不存在相互等待,这里处理的是保存在无序广播调度队列mParallelBroadcasts中的广播发送任务,// 即把保存在无序广播调度队列mParallelBroadcasts中的广播发送给它的目标广播接收者处理while (mParallelBroadcasts.size() > 0) {// 首先获取保存无序广播调度队列mParallelBroadcasts中的每一个BroadcastRecord对象r = mParallelBroadcasts.remove(0);r.dispatchTime = SystemClock.uptimeMillis();r.dispatchClockTime = System.currentTimeMillis();//获取BroadcastRecord中的所有广播接收者的数量final int N = r.receivers.size();for (int i = 0; i < N; i++) {Object target = r.receivers.get(i);     // 通过deliverToRegisteredReceiverLocked调用ActivityThread.scheduleRegisteredReceiver处理广播deliverToRegisteredReceiverLocked(r, (BroadcastFilter) target, false, i);}addBroadcastToHistoryLocked(r);} ...
}

上面这段代码逻辑:对并行处理列表中的广播调用deliverToRegisteredReceiverLocked将每一个无序广播发送给每一个广播接收者,异步处理广播。注意入参ordered值为false

//执行动态注册的广播接收者的发送接受过程
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,BroadcastFilter filter, boolean ordered, int index) {...// order广播,所有的接收者需要依次以一种同步的方式发送广播,// 可以看到order广播在BroadcastRecord保存了几个状态if (ordered) {// IBinder类型,代表当前的接收者r.receiver = filter.receiverList.receiver.asBinder();// 当前正在处理的BroadcastFilter,和上面的receiver是对应好的r.curFilter = filter;filter.receiverList.curBroadcast = r;r.state = BroadcastRecord.CALL_IN_RECEIVE;if (filter.receiverList.app != null) {r.curApp = filter.receiverList.app;filter.receiverList.app.curReceiver = r;mService.updateOomAdjLocked(r.curApp);}}try {if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {// Skip delivery if full backup in progress// If it's an ordered broadcast, we need to continue to the next receiver.// 如果正在备份或者恢复备份跳过,// 如果是一个有序广播,则执行下一个广播if (ordered) {skipReceiverLocked(r);}} else {// 如果不需要进行权限检查或者通过权限检查,调用performReceiveLocked发送广播performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,new Intent(r.intent), r.resultCode, r.resultData,r.resultExtras, r.ordered, r.initialSticky, r.userId);}if (ordered) {r.state = BroadcastRecord.CALL_DONE_RECEIVE;}} catch (RemoteException e) {...}
}

在后面【有序广播+动态注册】的处理也会走到这个函数deliverToRegisteredReceiverLocked,因此从这个函数的入参开始加ordered来进行区分,上面这段代码最终会走到performReceiveLocked方法中,继续往下看。

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) {// 因为是动态注册广播,所以不为空// If we have an app thread, do the call through that so it is// correctly ordered with other one-way calls.try {// 这里scheduleRegisteredReceiver函数是一个Binder调用,注释上面说的很清楚,// 调用ApplicationThread对象的Binder代理对象的函数来向它发送广播app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.repProcState);} catch (RemoteException ex) {// Failed to call into the process. It's either dying or wedged. Kill it gently.synchronized (mService) {app.scheduleCrash("can't deliver broadcast");}throw ex;}} else {// Application has died. Receiver doesn't exist.throw new RemoteException("app.thread must not be null");}} else {// 直接调用与它关联的一个InnerReceiver对象的Binder代理对象的成员函数performReceive来向它发送广播receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);}
}
  • app不为空,表示进程已经启动,调用ActivityThread.scheduleRegisteredReceiver发送当前广播;
  • 否则直接调用receiver.performReceive方法发送广播。因为当前分析的是动态注册的广播接收器,所以继续看scheduleRegisteredReceiver这个方法。

调用ActivityThread的scheduleRegisteredReceiver方法

/frameworks/base/core/java/android/app/ActivityThread.java

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,int resultCode, String dataStr, Bundle extras, boolean ordered,boolean sticky, int sendingUser, int processState) throws RemoteException {updateProcessState(processState, false);receiver.performReceive(intent, resultCode, dataStr, extras, ordered,sticky, sendingUser);
}

从上面的代码里看,最后走到了InnerReceiver中的performReceive方法中,继续往下看。

调用LoadedApk#ReceiverDispatcher#InnerReceiver的performReceive方法

/frameworks/base/core/java/android/app/LoadedApk.java#ReceiverDispatcher#InnerReceiver

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;}@Overridepublic void performReceive(Intent intent, int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) {final LoadedApk.ReceiverDispatcher rd;if (intent == null) {rd = null;} else {//获取rd的实例对象rd = mDispatcher.get();}//判断是否为空if (rd != null) {//存在广播,rd 不为空,调用LoadedApk.ReceiverDispatcher对象的performReceive方法rd.performReceive(intent, resultCode, data, extras,ordered, sticky, sendingUser);} else {IActivityManager mgr = ActivityManager.getService();try {if (extras != null) {extras.setAllowFds(false);}//不存在广播,rd为空,调用activityManagerService对象的finishReceiver结束广播的发送接受过程mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}}
}

LoadedApk.ReceiverDispatcher对象封装了广播接收者,如果该广播接收者注册了,那么该对象就会存在,这里就会调用LodedApk.ReceiverDispatcher.performReceive,否则就会调用AMS.finishReceiver方法,这里我们先看存在广播的情况。

调用LodedApk#ReceiverDispatcher的performReceive方法

/frameworks/base/core/java/android/app/LoadedApk.java#ReceiverDispatcher

public void performReceive(Intent intent, int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) {final Args args = new Args(intent, resultCode, data, extras, ordered, sticky, sendingUser);// 将args对象post到主线程的消息队列里面,作为一个Runnable调度而不是在handleMessage中处理,// 因此这里不久后会调用Args.run函数if (intent == null || !mActivityThread.post(args.getRunnable())) {if (mRegistered && ordered) {IActivityManager mgr = ActivityManager.getService();args.sendFinished(mgr);}}
}
  • 首先将参数Intent所描述的一个广播封装成一个Args对象,然后将这个Args对象封装成一个消息对象,然后将这个消息对象发送到应用程序主线程的消息队列中;

  • 该对象实现了Runnable接口,在if句中调用post方法,最终会调用Args中的run方法。

  • 如果是【有序广播+动态注册】会走到sendFinished函数,通知AMS,它前面转发过来的有序广播已经处理完了,这时AMS就可以继续将这个有序广播转发给下一个目标广播接收者了。这也是实现串行的原因,当前的发出了,才会通知进行下一个进行。而并发的是直接循环post然后完事了。

调用LodedApk#ReceiverDispatcher#Args的getRunnable方法

final class Args extends BroadcastReceiver.PendingResult {...public final Runnable getRunnable() {return () -> {// mReceiver指向一个广播接收者final BroadcastReceiver receiver = mReceiver;final boolean ordered = mOrdered;final IActivityManager mgr = ActivityManager.getService();final Intent intent = mCurIntent;mCurIntent = null;mDispatched = true;mPreviousRunStacktrace = new Throwable("Previous stacktrace");if (receiver == null || intent == null || mForgotten) {if (mRegistered && ordered) {sendFinished(mgr);}return;}try {// 这里处理的是动态广播接收者,默认认为接收者BroadcastReceiver已经存在ClassLoader cl = mReceiver.getClass().getClassLoader();intent.setExtrasClassLoader(cl);intent.prepareToEnterProcess();setExtrasClassLoader(cl);receiver.setPendingResult(this);// 接收广播receiver.onReceive(mContext, intent);                 } catch (Exception e) {// 检查当前广播是否是有序广播,并且广播接收者是否已经注册到AMS中if (mRegistered && ordered) {// 通知AMS,它前面转发过来的有序广播已经处理完了,这时AMS就可以继续将这个有序广播// 转发给下一个目标广播接收者了sendFinished(mgr);}if (mInstrumentation == null || !mInstrumentation.onException(mReceiver, e)) {throw new RuntimeException("Error receiving broadcast " + intent + " in " + mReceiver, e);}}// 然后调用BroadcastReceiver.PendingResult.finish函数,也就是下面的finish函数if (receiver.getPendingResult() != null) {finish();}Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);};}}

在run方法中调用BroadcastReceiver.onReceive方法,这样就会执行完一次无序广播的发送过程。从这里也能看出为什么并发队列中的处理是并行的,因为最后是新建了一个Runnable来处里,postRunnable后就可以执行下一个了,不会堵塞。

4.2.2 BroadcastQueue有序列表广播发送流程

4.2.1 讲了BroadcastQueue并发列表广播发送流程,我们把逻辑在移回去,看下有序广播发送流程。 processNextBroadcastLocked这个函数逻辑有点长,拆开来看下。

//参数fromMsg是用来描述processNextBroadcast是否是被handleMessage触发的
final void processNextBroadcast(boolean fromMsg) {synchronized (mService) {processNextBroadcastLocked(fromMsg, false);}
}
// 广播发送的核心内容
final void processNextBroadcastLocked(boolean fromMsg) {BroadcastRecord r;mService.updateCpuStats();// 否是来自handleMessage的BROADCAST_INTENT_MSG类型消息if (fromMsg) {mBroadcastsScheduled = false;}// 无序广播的处理流程...//开始有序广播的处理流程// Now take care of the next serialized one...// If we are waiting for a process to come up to handle the next// broadcast, then do nothing at this point.  Just in case, we// check that the process we're waiting for still exists.   // 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;}// 如果应用已经启动,会调用AMS的函数来处理静态广播,这里直接returnif (!isDead) {// It's still alive, so keep waitingreturn;} else {mPendingBroadcast.state = BroadcastRecord.IDLE;mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;mPendingBroadcast = null;}}
}

上面这段代码主要是处理静态注册广播,且注册进程没有启动的广播处理逻辑。mPendingBroadcast对象是用来描述一个正在等待静态注册的目标广播接收者启动起来的广播转发任务。

从前面可知,有序广播调度队列mOrderedBroadcast列表中的目标广播接收者有可能是静态注册的,而这些静态注册的目标广播接收者可能还没有启动起来,因此AMS将一个广播发送给它们处理时,首先将它们启动起来。

如果进程已经起来,会调用AMS的函数来处理静态广播,这里直接return。

	boolean looped = false;// 这里的do-while只会从mOrderedBroadcasts中取出第一个BroadcastRecord进行后续的处理!do {// 判断有序广播调度队列mOrderedBroadcasts是否还有需要处理的广播// 如果长度为0,则说明调度队列mOrderedBroadcasts中的广播已经全部处理完成if (mOrderedBroadcasts.size() == 0) {// No more broadcasts pending, so all done!mService.scheduleAppGcsLocked();if (looped) {// If we had finished the last ordered broadcast, then// make sure all processes have correct oom and sched// adjustments.mService.updateOomAdjLocked();}return;}// 如果没有处理完成,则继续取出mOrderedBroadcasts中的第一个BroadcastRecordr = mOrderedBroadcasts.get(0);boolean forceReceive = false;// Ensure that even if something goes awry with the timeout// detection, we catch "hung" broadcasts here, discard them,// and continue to make progress.//// This is only done if the system is ready so that PRE_BOOT_COMPLETED// receivers don't get executed with timeouts. They're intended for// one time heavy lifting after system upgrades and can take// significant amounts of time.// 获取广播转发任务的目标广播接收者的个数int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;if (mService.mProcessesReady && r.dispatchTime > 0) {long now = SystemClock.uptimeMillis();if ((numReceivers > 0) && (now > r.dispatchTime + (2 * mTimeoutPeriod * numReceivers))) {//出现超时,强制结束broadcastTimeoutLocked(false); // forcibly finish this broadcast// 重置参数,继续处理有序广播调度队列mOrderedBroadcasts的下一个广播转发任务forceReceive = true;r.state = BroadcastRecord.IDLE;}}if (r.state != BroadcastRecord.IDLE) {return;}// 检测广播转发任务是否正在处理中,即AMS正在将一个有序广播转发给它的前一个目标广播接收处理者,// 如果是,AMS就会等待这个目标广播接收者处理完该有序广播,然后再转发给下一个广播接收者处理if (r.receivers == null || r.nextReceiver >= numReceivers|| r.resultAbort || forceReceive) {// No more receivers for this broadcast!  Send the final// result if requested...if (r.resultTo != null) {try {performReceiveLocked(r.callerApp, r.resultTo,new Intent(r.intent), r.resultCode,r.resultData, r.resultExtras, false, false, r.userId);// Set this to null so that the reference// (local and remote) isn't kept in the mBroadcastHistory.r.resultTo = null;} catch (RemoteException e) {r.resultTo = null;}}// 调用函数cancelBroadcastTimeoutLocked来删除前面发送到AMS所运行在的线程的// 消息队列中的一个BROADCAST_TIMEOUT_MSG消息cancelBroadcastTimeoutLocked();// ... and on to the next...addBroadcastToHistoryLocked(r);if (r.intent.getComponent() == null && r.intent.getPackage() == null&& (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {// This was an implicit broadcast... let's record it for posterity.mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,r.manifestCount, r.manifestSkipCount, r.finishTime - r.dispatchTime);}// BroadcastRecord处理完移除mOrderedBroadcasts.remove(0);r = null;looped = true;continue;}} while (r == null);// 如果第一次取出的r不为空,则退出循环

上面这段代码是在处理广播前对一些特殊情况进行处理,例如,处理列表为空,直接返回;当前广播正在处理中返回;下一个接收者为空或者当前接受者设置了resultAbort 等异常情况处理。

4.2.2.1 动态接收器的处理

	// Get the next receiver...//获取下一个将要处理的广播接收者在其列表中的位置int recIdx = r.nextReceiver++;// Keep track of when this receiver started, and make sure there// is a timeout message pending to kill it if need be.// 保存当前时间r.receiverTime = SystemClock.uptimeMillis();if (recIdx == 0) {// 表示第一个开始处理的接收者,也就是BroadcastRecord对象r所描述的广播任务刚被处理// 接收者开始处理的时间戳,也就是这个接收者开始处理了,要记录开始时间来计算是否超过超时时间// 也就是说这是BroadcastRecord中第一个接收者开始被处理的时间戳,也就是上面BroadcastRecord// 超时的起点,可以看到上面超时比较的时候用的就是r.dispatchTimer.dispatchTime = r.receiverTime;r.dispatchClockTime = System.currentTimeMillis();}if (!mPendingBroadcastTimeoutMessage) {long timeoutTime = r.receiverTime + mTimeoutPeriod;// 设置超时,传入参数是r.receiverTime + mTimeoutPeriod,也就是开始时间加上超时时间// mTimeoutPeriod,mTimeoutPeriod初始化是在BroadcastQueue初始化的时候传入的,// 也就是在AMS(AMS构造函数中)中初始化mFgBroadcastQueue和mBgBroadcastQueue时传入的// BROADCAST_FG_TIMEOUT = 10 * 1000和BROADCAST_BG_TIMEOUT = 60 * 1000,// 这里开始埋了ANR的雷setBroadcastTimeoutLocked(timeoutTime);}final BroadcastOptions brOptions = r.options;// 得到下一个广播接收者final Object nextReceiver = r.receivers.get(recIdx);// 如果当前nextReceiver是一个BroadcastFilter类型,说明是一个动态注册接收者,不需要启动一个进程if (nextReceiver instanceof BroadcastFilter) {// Simple case: this is a registered receiver who gets// a direct call.BroadcastFilter filter = (BroadcastFilter) nextReceiver;// 调用deliverToRegisteredReceiverLocked向所有的receivers发送广播// 将它所描述的每一个无序广播发送给每一个广播接收者,异步处理广播// 通过deliverToRegisteredReceiverLocked调用ActivityThread.scheduleRegisteredReceiver处理广播deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);// 检查BroadcastRecord对象r所描述的广播转发任务是否用来转发无序广播的。if (r.receiver == null || !r.ordered) {// 设置IDLE状态,表示AMS不需要等待它的前一个目标广播接收者处理完成一个广播就可以将该广播// 继续发送给它的下一个目标广播接收者处理r.state = BroadcastRecord.IDLE;// 调用下面函数就是为了将一个广播继续发送给BroadcastRecord对象r所描述的广播转发任务的// 下一个目标广播接收者处理的scheduleBroadcastsLocked();} else {if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {scheduleTempWhitelistLocked(filter.owningUid,brOptions.getTemporaryAppWhitelistDuration(), r);}}return;}

上面代码开始对广播进行处理,先进行了计时操作,然后根据接收者不同,先对动态注册接收器进行处理(BroadcastFilter类型)。逻辑最后通过deliverToRegisteredReceiverLocked调用ActivityThread.scheduleRegisteredReceiver处理广播。上面有分析这个函数,此时入参orderedtrue,用来执行动态注册的广播接收者的发送接收过程。

4.2.2.2 静态接收器的处理

接下来看下静态注册器的处理:

    // Hard case: need to instantiate the receiver, possibly// starting its application process to host it.// 如果上面if没有进行拦截,说明不是广播接收者动态注册的,而应该是静态注册的// 此时进程可能没有启动ResolveInfo info = (ResolveInfo) nextReceiver;ComponentName component = new ComponentName(info.activityInfo.applicationInfo.packageName,info.activityInfo.name);// 是否跳过该广播接收者不处理...// This is safe to do even if we are skipping the broadcast, and we need// this information now to evaluate whether it is going to be allowed to run.final int receiverUid = info.activityInfo.applicationInfo.uid;// If it's a singleton, it needs to be the same app or a special appif (r.callingUid != Process.SYSTEM_UID && isSingleton&& mService.isValidSingletonCall(r.callingUid, receiverUid)) {info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);}// 得到ResolveInfo对象info所描述的广播接收者的android:process属性值,// 即它需要运行在的应用程序进程的名称,并且保存在变量targetProcess中String targetProcess = info.activityInfo.processName;// 获取当前广播接收者的进程记录,也就是该静态广播接收者是否已经运行ProcessRecord app = mService.getProcessRecordLocked(targetProcess,info.activityInfo.applicationInfo.uid, false);...// Is this receiver's application already running?// 如果当前进程已经运行,则直接发给该进程,然后返回if (app != null && app.thread != null && !app.killed) {try {app.addPackage(info.activityInfo.packageName,info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);// app进程存在,通过processCurBroadcastLocked -> ActivityThread.scheduleReceiver -> receiver.onReceive处理当前广播 processCurBroadcastLocked(r, app, skipOomAdj);// order广播是一种同步处理方式,因此处理完可以直接returnreturn;} catch (RemoteException e) {...} catch (RuntimeException e) {// If some unexpected exception happened, just skip// this broadcast.  At this point we are not in the call// from a client, so throwing an exception out from here// will crash the entire system instead of just whoever// sent the broadcast.logBroadcastReceiverDiscardLocked(r);finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();// We need to reset the state if we failed to start the receiver.r.state = BroadcastRecord.IDLE;return;}// If a dead object exception was thrown -- fall through to// restart the application.}// 如果app进程不存在,会先创建该进程if ((r.curApp = mService.startProcessLocked(targetProcess,info.activityInfo.applicationInfo, true,r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,"broadcast", r.curComponent,(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))== null) {// 如果启动失败logBroadcastReceiverDiscardLocked(r);// 结束广播发送任务finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();r.state = BroadcastRecord.IDLE;return;}// 将BroadcastRecord赋值为mPendingBroadcast,等待应用启动完成后处理// 正在启动接收者进程,将正在启动的BroadcastRecord记录存储到mPendingBroadcast中,同时将当前正在// 启动的接收者进程在所有接收者中的索引存储到mPendingBroadcastRecvIndex,如果当前广播接收者处理// 完,需要继续从mPendingBroadcastRecvIndex计算到下一个接收者发送当前广播mPendingBroadcast = r;mPendingBroadcastRecvIndex = recIdx;

上面代码是对静态广播的处理,静态广播又分为两种,静态接收者所在进程是启动状态,静态接收者所在进程是未启动状态。根据所在进程的启动状态分别来进行处理。

app进程存在,通过processCurBroadcastLocked -> ActivityThread.scheduleReceiver -> receiver.onReceive处理当前广播。

app进程不存在,会先创建该进程。

APP存在,调用BroadcastQueue的processCurBroadcastLocked方法处理有序广播

private final void processCurBroadcastLocked(BroadcastRecord r,ProcessRecord app, boolean skipOomAdj) throws RemoteException {if (app.thread == null) {throw new RemoteException();}if (app.inFullBackup) {skipReceiverLocked(r);return;}// 将进程的相关信息写入当前BroadcastRecord中相关的接收者r.receiver = app.thread.asBinder();r.curApp = app;app.curReceivers.add(r);app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);mService.updateLruProcessLocked(app, false, null);if (!skipOomAdj) {mService.updateOomAdjLocked();}// Tell the application to launch this receiver.r.intent.setComponent(r.curComponent);boolean started = false;try {mService.notifyPackageUse(r.intent.getComponent().getPackageName(),PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);// 处理广播,等待接收进程的返回app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,app.repProcState);started = true;} finally {if (!started) {r.receiver = null;r.curApp = null;app.curReceivers.remove(r);}}
}

APP存在,调用ApplicationThread的scheduleReceiver方法

// 处理应用进程中接收到的静态广播消息,实际处理该广播的是ActivityThread.handleReceiver函数
public final void scheduleReceiver(Intent intent, ActivityInfo info,CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,boolean sync, int sendingUser, int processState) {updateProcessState(processState, false);ReceiverData r = new ReceiverData(intent, resultCode, data, extras,sync, false, mAppThread.asBinder(), sendingUser);r.info = info;r.compatInfo = compatInfo;sendMessage(H.RECEIVER, r);
}class H extends Handler {...public void handleMessage(Message msg) {switch (msg.what) {...case RECEIVER://该消息最终会触发handleReceiver方法handleReceiver((ReceiverData)msg.obj);break;    ...   		}
}

APP存在,调用ApplicationThread的handleReceiver方法

    private void handleReceiver(ReceiverData data) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.unscheduleGcIdler();// 1) 创建BroadcastReceiver对象// 这里处理的是静态广播接收者,默认认为接收者BroadcastReceiver对象不存在// 每次接受都会创建一个新的BroadcastReceiver对象String component = data.intent.getComponent().getClassName();LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);IActivityManager mgr = ActivityManager.getService();Application app;BroadcastReceiver receiver;ContextImpl context;try {// 首先从AMS传递的intent中获取当前处理该广播的组件名称,然后通过反射创建一个BroadcastReceiver// 对象,从这里可以看出来,静态广播处理的时候,每次都会创建一个新的BroadcastReceiver对象;// 创建Application对象,如果进程已经启动,Application对象已经创建app = packageInfo.makeApplication(false, mInstrumentation);context = (ContextImpl) app.getBaseContext();if (data.info.splitName != null) {context = (ContextImpl) context.createContextForSplit(data.info.splitName);}java.lang.ClassLoader cl = context.getClassLoader();data.intent.setExtrasClassLoader(cl);data.intent.prepareToEnterProcess();data.setExtrasClassLoader(cl);receiver = packageInfo.getAppFactory().instantiateReceiver(cl, data.info.name, data.intent);} catch (Exception e) {data.sendFinished(mgr);throw new RuntimeException("Unable to instantiate receiver " + component+ ": " + e.toString(), e);}// 2) 执行onReceive函数try {sCurrentBroadcastIntent.set(data.intent);// 调用接收者的onReceive方法,这里还调用了setPendingResult方法,详细内容请看BroadcastReceiver.goAsync方法。receiver.setPendingResult(data);receiver.onReceive(context.getReceiverRestrictedContext(),data.intent);} catch (Exception e) {data.sendFinished(mgr);if (!mInstrumentation.onException(receiver, e)) {throw new RuntimeException("Unable to start receiver " + component+ ": " + e.toString(), e);}} finally {sCurrentBroadcastIntent.set(null);}// 3) 向AMS发送处理结束消息if (receiver.getPendingResult() != null) {data.finish();}}

上面代码主要干了三件事情:

  • 创建BroadcastReceiver对象;
  • 执行onReceive函数;
  • 向AMS发送处理结束消息,通知将当前广播发送给下一个接收者;

APP未启动,先创建该进程
如果静态广播接收者进程尚未启动,会直接调用AMS的startProcessLocked函数启动该接收者进程,并将当前正在等待进程
启动的BroadcastRecord存储到mPendingBroadcast里面,这个就是静态广播拉起应用的原理。

在app进程启动之后,会先调用application的attachonCreate方法,然后才会调用ActivityManagerService的sendPendingBroadcastsLocked方法。

boolean sendPendingBroadcastsLocked(ProcessRecord app) {boolean didSomething = false;for (BroadcastQueue queue : mBroadcastQueues) {//调用BroadcastQueue的sendPendingBroadcastsLocked方法didSomething |= queue.sendPendingBroadcastsLocked(app);}return didSomething;
}

mBroadcastQueues是包含前台和后台广播队列,这里分别调用前台和后台优先级广播的BroadcastQueue.sendPendingBroadcastsLocked方法。

调用BroadcastQueue的sendPendingBroadcastsLocked方法

    // 未启动进程的广播接收者需要先启动进程,最后到达这个函数public boolean sendPendingBroadcastsLocked(ProcessRecord app) {boolean didSomething = false;// 前面分析mPendingBroadcast用于存储当前正在等待进程启动的BroadcastRecordfinal BroadcastRecord br = mPendingBroadcast;if (br != null && br.curApp.pid > 0 && br.curApp.pid == app.pid) {if (br.curApp != app) {Slog.e(TAG, "App mismatch when sending pending broadcast to "+ app.processName + ", intended target is " + br.curApp.processName);return false;}try {// 启动完成设置为nullmPendingBroadcast = null;// 调用processCurBroadcastLocked方法进行处理processCurBroadcastLocked(br, app, false);didSomething = true;} catch (Exception e) {Slog.w(TAG, "Exception in new application when starting receiver "+ br.curComponent.flattenToShortString(), e);logBroadcastReceiverDiscardLocked(br);finishReceiverLocked(br, br.resultCode, br.resultData,br.resultExtras, br.resultAbort, false);scheduleBroadcastsLocked();// We need to reset the state if we failed to start the receiver.br.state = BroadcastRecord.IDLE;throw new RuntimeException(e.getMessage());}}return didSomething;}

这里是找到等待处理的广播并且判断是否为空,以及是否和当前进程的pid相同,也就是不是这个进程的等待广播,如果是就调用processCurBroadcastLocked方法进行处理,后面的处理和上面app已启动的流程一致了。

5、流程总结

上面流程大体流程图:
请添加图片描述
执行时序图:

在这里插入图片描述

三、总结

看完源码再来看下一开始的问题:
1、广播为啥会阻塞呢?发送给接收器就行了,为啥还要等着接收器处理完才处理下一个?

从上面的源码分析可知,广播的处理分为并行和有序两个队列,出问题的无序广播静态接收器放在了有序处理列表中,而有序处理列表的执行是串行的,只有前面的执行完,才会轮到下一个处理,所以前面的广播如果在onReceive中有耗时操作,后面的广播就会堵塞。

2、 由普通的后台广播改为前台广播后,为啥处理的会更快?

在上面源码中有个变量的注释:mTimeoutPeriod。这个变量初始化是在BroadcastQueue初始化的时候传入的,也就是在AMS(AMS构造函数中)中初始化mFgBroadcastQueue和mBgBroadcastQueue时传入的,前台广播的超时时间是10s,后台的超时时间是60s。

BROADCAST_FG_TIMEOUT = 10 * 1000 
BROADCAST_BG_TIMEOUT = 60 * 1000

后台广播的设计思想就是当前应用优先,尽可能多让收到广播的应用有充足的时间把事件做完。

而前台广播的目的是紧急通知,设计上就倾向于当前应用赶快处理完,尽快传给下一个。

也就是说在设计上前台广播主要用于响应性能较高的场景,因为ANR时间是10s,所以开发设计的时候应该尽可能少用。因为前台广播使用的比较少,所以队列相对空闲,响应速度快。

3、对照源码分析总结:

  • 前后台队列都有自己并行和有序广播队列,互相不影响;
  • 并行队列里是无序广播+动态注册接收者;
  • 有序队列里是有序广播+动态接收者和静态接收者,静态接收者默认就是有序的;
  • 有序广播+动态接收者执行优于静态接收者先执行,综合起来就是广播相同的情况下,动态接收器优于静态接收器
  • Android版本高的,很多系统广播只支持动态注册,静态注册的话收不到广播,例如:息屏亮屏广播。因为静态注册的话,发广播的时候会把所有注册未启动的app全部拉起来,静态处理器又默认串行处理,增加了广播的处理时间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/45345.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM前世今生之JVM内存模型

JVM内存模型所指的是JVM运行时区域&#xff0c;该区域分为两大块 线程共享区域 堆内存、方法区&#xff0c;即所有线程都能访问该区域&#xff0c;随着虚拟机和GC创建和销毁 线程独占区域 虚拟机栈、本地方法栈、程序计数器&#xff0c;即每个线程都有自己独立的区域&#…

帆软大屏2.0企业制作

&#xfffc; 数字化观点中心 / 当前页 如何从0-1制作数据大屏&#xff0c;我用大白话给你解释清楚了 文 | 商业智能BI相关文章 阅读次数&#xff1a;18,192 次浏览 2023-06-08 11:51:49 好莱坞大片《摩天营救》中有这么一个场景&#xff1a; &#xfffc; 你可以看见反派大b…

使用Nginx调用网关,然后网关调用其他微服务

问题前提&#xff1a;目前我的项目是已经搭建了网关根据访问路径路由到微服务&#xff0c;然后现在我使用了Nginx将静态资源都放在了Nginx中&#xff0c;然后我后端定义了一个接口访问一个html页面&#xff0c;但是html页面要用到静态资源&#xff0c;这个静态资源在我的后端是…

PyTorch模型性能分析与优化

动动发财的小手&#xff0c;点个赞吧&#xff01; 训练深度学习模型&#xff0c;尤其是大型模型&#xff0c;可能是一项昂贵的支出。我们可以使用的管理这些成本的主要方法之一是性能优化。性能优化是一个迭代过程&#xff0c;我们不断寻找提高应用程序性能的机会&#xff0c;然…

Springboot 实践(10)spring cloud 与consul配置运用之服务的注册与发现

前文讲解&#xff0c;完成了springboot、spring security、Oauth2.0的继承&#xff0c;实现了对系统资源的安全授权、允许获得授权的用户访问&#xff0c;也就是实现了单一系统的全部技术开发内容。 Springboot是微服务框架&#xff0c;单一系统只能完成指定系统的功能&#xf…

NSSCTF之Misc篇刷题记录(14)

[SWPUCTF] 2021新生赛之Crypto篇刷题记录① [UUCTF 2022 新生赛]王八快跑[安洵杯 2020]BeCare4[HDCTF 2023]ExtremeMisc[SUCTF 2018 招新赛]follow me[SUCTF 2018 招新赛]佛家妙语 NSSCTF平台&#xff1a;https://www.nssctf.cn/ PS&#xff1a;记得所有的flag都改为NSSCTF […

【Linux取经路】探索进程状态之僵尸进程 | 孤儿进程

文章目录 一、进程状态概述1.1 运行状态详解1.2 阻塞状态详解1.3 挂起状态详解 二、具体的Linux操作系统中的进程状态2.1 Linux内核源代码2.2 查看进程状态2.3 D磁盘休眠状态(Disk sleep)2.4 T停止状态(stopped) 三、僵尸进程3.1 僵尸进程危害总结 四、孤儿进程五、结语 一、进…

C++初阶——string(字符数组),跟C语言中的繁琐设计say goodbye

前言&#xff1a;在日常的程序设计中&#xff0c;我们会经常使用到字符串。比如一个人的身份证号&#xff0c;家庭住址等&#xff0c;只能用字符串表示。在C语言中&#xff0c;我们经常使用字符数组来存储字符串&#xff0c;但是某些场景(比如插入&#xff0c;删除)下操作起来很…

git版本管理加合并笔记

1.创建空文件夹&#xff0c;右键Bash here打开 2.打开链接&#xff0c;点击克隆下载&#xff0c;复制SSH链接 3.输入git SSH链接 回车 遇到问题&#xff1a; 但明明我已经有权限了&#xff0c; 还是蹦出个这 4.换成https在桌面上进行克隆仓库就正常了 5.去vscode里改东西 …

删除远程桌面的下拉框ip地址

原因&#xff1a; 如下图&#xff0c;有时候想清理掉无法连接的IP。 方法&#xff1a; 一、进入 注册表编辑器 进入方法&#xff1a;一下两个方法都可以使用。 1. 在win10里面直接搜索 注册表编辑器&#xff0c;然后打开 2. 打开 运行&#xff08;Win R&#xff09;&#xff…

文件同步工具rsync

文章目录 作用特性安装命令服务端启动增加安全认证及免密登录 实时推送源服务器配置结合inotify实现实时推送 参数详解 学些过程中遇到的问题 作用 rsync是linux系统下的数据镜像备份工具。使用快速增量备份工具Remote Sync可以远程同步&#xff0c;支持本地复制&#xff0c;或…

蛊卦-拨乱反正

目录 前言 卦辞 爻辞 总结 前言 题外话&#xff0c;今天占卜时&#xff0c;看错了&#xff0c;以为占到了蛊卦&#xff08;后续会对自己的占卦经历进行补充&#xff0c;不断完善这个易经学习的专栏&#xff09;&#xff0c;那顺便就学习一下蛊卦&#xff0c;蛊惑人心&#…

axios使用axiosSource.cancel取消请求后怎么恢复请求,axios取消请求和恢复请求实现

在前端做大文件分片上传&#xff0c;或者其它中断请求时&#xff0c;需要暂停或重新请求&#xff0c;比如这里大文件上传时&#xff0c;可能会需要暂停、继续上传&#xff0c;如下GIF演示&#xff1a; 这里不详细说文件上传的处理和切片细节&#xff0c;后续有时间在出一篇&a…

2023国赛数学建模B题思路模型代码 高教社杯

本次比赛我们将会全程更新思路模型及代码&#xff0c;大家查看文末名片获取 之前国赛相关的资料和助攻可以查看 2022数学建模国赛C题思路分析_2022国赛c题matlab_UST数模社_的博客-CSDN博客 2022国赛数学建模A题B题C题D题资料思路汇总 高教社杯_2022国赛c题matlab_UST数模社…

AI 绘画Stable Diffusion 研究(十二)SD数字人制作工具SadTlaker插件安装教程

免责声明: 本案例所用安装包免费提供&#xff0c;无任何盈利目的。 大家好&#xff0c;我是风雨无阻。 想必大家经常看到&#xff0c;无论是在产品营销还是品牌推广时&#xff0c;很多人经常以数字人的方式来为自己创造财富。而市面上的数字人收费都比较昂贵&#xff0c;少则几…

使用yolov5进行安全帽检测填坑指南

参考项目 c​​​​​​​​​​​​​​GitHub - PeterH0323/Smart_Construction: Base on YOLOv5 Head Person Helmet Detection on Construction Sites&#xff0c;基于目标检测工地安全帽和禁入危险区域识别系统&#xff0c;&#x1f680;&#x1f606;附 YOLOv5 训练自己的…

Spring MVC 中的常见注解的用法

目录 认识 Spring MVC什么是 Spring MVCMVC 的定义 Spring MVC 注解的运用1. Spring MVC 的连接RequestMapping 注解 2. 获取参数获取单个参数获取多个参数传递对象表单传参后端参数重命名RequestBody 接收 JSON 对象PathVariable 获取 URL 中的参数上传文件 RequestPart获取 C…

C++系列-内存模型

内存模型 内存模型四个区代码区全局区栈区堆区内存开辟和释放在堆区开辟数组 内存模型四个区 不同区域存放的数据生命周期是不同的&#xff0c;更为灵活。 代码区&#xff1a;存放函数体的二进制代码&#xff0c;操作系统管理。全局区&#xff1a;存放全局变量&#xff0c;常…

AutoSAR配置与实践(基础篇)2.5 RTE对数据一致性的管理

传送门 点击返回 ->AUTOSAR配置与实践总目录 AutoSAR配置与实践&#xff08;基础篇&#xff09;2.5 RTE对数据一致性的管理 一、 数据一致性问题引入二、 数据一致性的管理2.1 RTE管理 (SWC间)2.2 中断保护 (SWC内)2.3 变量保护IRVS (SWC内)2.4 Task分配2.5 任务抢占控制 一…

44、TCP报文(二)

接上节内容&#xff0c;本节我们继续TCP报文首部字段含义的学习。上节为止我们学习到“数据偏移”和“保留”字段。接下来我们学习后面的一些字段&#xff08;暂不包含“检验和”的计算方法和选项字段&#xff09;。 TCP首部结构&#xff08;续&#xff09; “数据偏移”和“保…