说说Android的广播(3) - 什么样的广播是并发的?
什么样的广播是并发的?
现在让我们开始破解Android中的一个trick,普通广播都是并发的吗?
带着这个问题,我们来看ActivityManagerService.broadcastIntentLocked中的实现逻辑。
broadcastIntentLocked中的细节很多,我们放到后面讲,我们先只把跟并发和串行队列有关的部分专门提炼出来。
receivers和registeredReceivers
画重点了,画重点了啊!下面我们要学习两个重要的概念,一个是receivers,另一个是registeredReceivers。这两个东西的区别真正决定了它是能并发接收到广播还是串行地接收广播。
通俗地讲,registeredReceivers就是通过Java代码在运行时注册的receiver,而receivers是连通过AndroidManifest.xml中注册的也算上。
如果想只发给动态注册的BroadcastReceiver,可以设置有个FLAG_RECEIVER_REGISTERED_ONLY属性。
在处理普通广播处理的流程时,就先定义了两个List,一个是receivers,另一个是registeredReceivers.
16820 // Figure out who all will receive this broadcast.
16821 List receivers = null;
16822 List<BroadcastFilter> registeredReceivers = null;
我们分别看看,它们都是在哪里被赋值的:
如刚才所说,只有在不设FLAG_RECEIVER_REGISTERED_ONLY的情况下,才需要查完整的receiver列表。
16823 // Need to resolve the intent to interested receivers...
16824 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
16825 == 0) {
16826 receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
16827 }
如果intent.getComponent() == null,则通过queryIntent方法来查询registeredReceivers。
16828 if (intent.getComponent() == null) {
...
16847 registeredReceivers = mReceiverResolver.queryIntent(intent,
16848 resolvedType, false, userId);
...
16850 }
好,两个receiver相关的列表都被赋值好了。下面揭晓奇迹的时候来了,究竟是哪个被翻牌子了呢?
16858 int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
16859 if (!ordered && NR > 0) {
16860 // If we are not serializing this broadcast, then send the
16861 // registered receivers separately so they don't wait for the
16862 // components to be launched.
16863 final BroadcastQueue queue = broadcastQueueForIntent(intent);
16864 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
16865 callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
16866 appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
16867 resultExtras, ordered, sticky, false, userId);
16868 if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
16869 final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
16870 if (!replaced) {
16871 queue.enqueueParallelBroadcastLocked(r);
16872 queue.scheduleBroadcastsLocked();
16873 }
16874 registeredReceivers = null;
16875 NR = 0;
16876 }
没错,是非ordered的registered receivers被并发执行了。
那么receivers是如何被处理的呢?一句话,被当成ordered处理了.
我们看代码:
16950 if ((receivers != null && receivers.size() > 0)
16951 || resultTo != null) {
16952 BroadcastQueue queue = broadcastQueueForIntent(intent);
16953 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
16954 callerPackage, callingPid, callingUid, resolvedType,
16955 requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
16956 resultData, resultExtras, ordered, sticky, false, userId);
...
16965 queue.enqueueOrderedBroadcastLocked(r);
16966 queue.scheduleBroadcastsLocked();
...
16968 }
ActivityManagerService.broadcastIntentLocked完整流程分析
上面我们是跳着看的,大家缺少一个完整的印象,让我们耐着性子把整个流程过一下吧。
private final int broadcastIntentLocked(ProcessRecord callerApp,String callerPackage, Intent intent, String resolvedType,IIntentReceiver resultTo, int resultCode, String resultData,Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {intent = new Intent(intent);// By default broadcasts do not go to stopped apps.intent.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);}if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,(sticky ? "Broadcast sticky: ": "Broadcast: ") + intent+ " ordered=" + ordered + " userid=" + userId);if ((resultTo != null) && !ordered) {Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");}userId = handleIncomingUser(callingPid, callingUid, userId,true, ALLOW_NON_FULL, "broadcast", callerPackage);
...
中间是一堆判断权限之类的操作,我们就略过不看了。
...final String action = intent.getAction();if (action != null) {switch (action) {case Intent.ACTION_UID_REMOVED:case Intent.ACTION_PACKAGE_REMOVED:case Intent.ACTION_PACKAGE_CHANGED:case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
...case Intent.ACTION_TIMEZONE_CHANGED:// If this is the time zone changed action, queue up a message that will reset// the timezone of all currently running processes. This message will get// queued up before the broadcast happens.mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);break;case Intent.ACTION_TIME_CHANGED:// If the user set the time, let all running processes know.final int is24Hour =intent.getBooleanExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1: 0;mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();synchronized (stats) {stats.noteCurrentTimeChangedLocked();}break;case Intent.ACTION_CLEAR_DNS_CACHE:mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);break;case Proxy.PROXY_CHANGE_ACTION:ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));break;}}
...
上面是一些特殊的广播的处理。
下面终于开始比较核心的逻辑了,去查找一下接收这个广播的接收者都是些什么吧。
// Figure out who all will receive this broadcast.List receivers = null;List<BroadcastFilter> registeredReceivers = null;// Need to resolve the intent to interested receivers...if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)== 0) {receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);}
FLAG_RECEIVER_REGISTERED_ONLY这个属性用于说明是否是支持在代码中动态注册的接收者,如果不是的话,那么要通过collectReceiverComponents方法去查一下哪些应用在AndroidManifest.xml中也注册了监听。
这么有趣的函数,我们跟起去看看吧。
ActivityManagerService.collectReceiverComponents
进入之前,我们先看看几个数据类的定义:
ResolveInfo类用来存储AndroidManifest.xml中<intent>
标签中对应的信息。
public class ResolveInfo implements Parcelable;
我们正式开始进入collectReceiverComponents:
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,int callingUid, int[] users) {List<ResolveInfo> receivers = null;try {HashSet<ComponentName> singleUserReceivers = null;boolean scannedFirstReceivers = false;for (int user : users) {// Skip users that have Shell restrictionsif (callingUid == Process.SHELL_UID&& getUserManagerLocked().hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {continue;}
下面调用到PMS去查询:
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
PMS中的这段代码是这样的,我们先不再往下细分析了,后面会讲注册的时候再提到。
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,int userId) {if (!sUserManager.exists(userId)) return Collections.emptyList();ComponentName comp = intent.getComponent();if (comp == null) {if (intent.getSelector() != null) {intent = intent.getSelector();comp = intent.getComponent();}}if (comp != null) {List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);ActivityInfo ai = getReceiverInfo(comp, flags, userId);if (ai != null) {ResolveInfo ri = new ResolveInfo();ri.activityInfo = ai;list.add(ri);}return list;}// readersynchronized (mPackages) {String pkgName = intent.getPackage();if (pkgName == null) {return mReceivers.queryIntent(intent, resolvedType, flags, userId);}final PackageParser.Package pkg = mPackages.get(pkgName);if (pkg != null) {return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,userId);}return null;}
}
我们回到collectReceiverComponents中继续往下看:
if (user != UserHandle.USER_OWNER && newReceivers != null) {// If this is not the primary user, we need to check for// any receivers that should be filtered out.for (int i=0; i<newReceivers.size(); i++) {ResolveInfo ri = newReceivers.get(i);if ((ri.activityInfo.flags&ActivityInfo.FLAG_PRIMARY_USER_ONLY) != 0) {newReceivers.remove(i);i--;}}}if (newReceivers != null && newReceivers.size() == 0) {newReceivers = null;}if (receivers == null) {receivers = newReceivers;} else if (newReceivers != null) {// We need to concatenate the additional receivers// found with what we have do far. This would be easy,// but we also need to de-dup any receivers that are// singleUser.if (!scannedFirstReceivers) {// Collect any single user receivers we had already retrieved.scannedFirstReceivers = true;for (int i=0; i<receivers.size(); i++) {ResolveInfo ri = receivers.get(i);if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);if (singleUserReceivers == null) {singleUserReceivers = new HashSet<ComponentName>();}singleUserReceivers.add(cn);}}}// Add the new results to the existing results, tracking// and de-dupping single user receivers.for (int i=0; i<newReceivers.size(); i++) {ResolveInfo ri = newReceivers.get(i);if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);if (singleUserReceivers == null) {singleUserReceivers = new HashSet<ComponentName>();}if (!singleUserReceivers.contains(cn)) {singleUserReceivers.add(cn);receivers.add(ri);}} else {receivers.add(ri);}}}}} catch (RemoteException ex) {// pm is in same process, this will never happen.}return receivers;
}
下面,我们回到broadcastIntentLocked中继续往下看:
if (intent.getComponent() == null) {if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {// Query one target user at a time, excluding shell-restricted usersUserManagerService ums = getUserManagerLocked();for (int i = 0; i < users.length; i++) {if (ums.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {continue;}List<BroadcastFilter> registeredReceiversForUser =mReceiverResolver.queryIntent(intent,resolvedType, false, users[i]);if (registeredReceivers == null) {registeredReceivers = registeredReceiversForUser;} else if (registeredReceiversForUser != null) {registeredReceivers.addAll(registeredReceiversForUser);}}} else {registeredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false, userId);}}final boolean replacePending =(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
终于快到我们最关心的逻辑部分了,大家仔细看啦!
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueing broadcast: " + intent.getAction()+ " replacePending=" + replacePending);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.
如果不是ordered,且注册这个事件的receiver队列不为空,我们就开始处理普通消息了!
final BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,resultExtras, ordered, sticky, false, userId);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
下面我们就将普通消息加入到并行消息队列里,enqueueParallelBroadcastLocked和scheduleBroadcastsLocked我们前面预备知识都讲过了,这里不再重复了。
if (!replaced) {queue.enqueueParallelBroadcastLocked(r);queue.scheduleBroadcastsLocked();}registeredReceivers = null;NR = 0;}
以上,并发消息处理正式结束。下面开始的是串行消息的处理,中间我们略去一段对于PACKAGE_ADDED消息发给新安装的应用这一段支线任务。
另外,有序队列中还有一件额外的事情需要做,有序么,要排序。
// Merge into one list.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) {// 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 ((receivers != null && receivers.size() > 0)|| resultTo != null) {BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, 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());boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);if (!replaced) {queue.enqueueOrderedBroadcastLocked(r);queue.scheduleBroadcastsLocked();}}return ActivityManager.BROADCAST_SUCCESS;
}
说说Android的广播(3) - 什么样的广播是并发的?相关推荐
- Android短信的发送和广播接收者实现短信的监听
Android短信的发送和广播接收者实现短信的监听 要注意Android清单中权限的设置以及广播的注册监听实现 以下就是 Android清单的XML AndroidManifest.xml < ...
- android 收不到短信广播,android – 短信广播接收器没有得到textmessage
我做了什么 大家好,我正在创建一个短信广播接收器,我刚刚用本教程建立了一个:Broadcasttutorial.在我完成代码之后,我更新了我的Manifest.之后我将短信从我的另一部手机发送到我的手 ...
- 【Android 电量优化】JobScheduler 源码分析 ( JobServiceContext 源码分析 | 闭环操作总结 | 用户提交任务 | 广播接收者接受相关广播触发任务执行 )★
文章目录 一.JobServiceContext 引入 二.JobServiceContext 源码分析 三.用户在应用层如何使用 JobScheduler 四.用户提交任务 五.广播接收者监听广播触 ...
- 【Android 电量优化】电量优化 ( 充电状态获取 | 主动获取充电状态 | 广播接受者监听充电状态 | 被动获取充电状态 | 注册空广播接受者获取历史广播 )
文章目录 一.获取充电状态 二.被动获取充电状态 三.主动获取充电状态 参考 Google 官方文档 : 优化电池续航时间 一.获取充电状态 在应用中执行某些操作 , 如软件云端备份 , 从服务器端获 ...
- Android 第二十课 广播机制(大喇叭)----发送自定义广播(包括发送标准广播和发送有序广播)
广播分为两种类型:标准广播和有序广播 我们来看一下具体这两者的具体区别: 1.发送标准广播 我们需要先定义一个广播接收器来准备接收此广播才行,否则也是白发. 新建一个MyBroadcastReceiv ...
- Android 第十九课 大喇叭--广播机制----动态注册监听网络变化与静态注册实现开机启动
为了便于进行 系统级别的消息通知,Android引入了一套广播消息机制. 1.广播机制简介: 因为Android中的每个应用程序都可以对自己感兴趣的广播尽心注册,这样程序只会接收自己所关心的广播内容, ...
- Android入门(11)| 全局广播与本地广播
文章目录 广播概念 接收广播 动态注册 实例 静态注册 实例 发送广播 发送标准广播 广播的跨进程特性 发送有序广播 本地广播 广播概念 Android 中的每个应用程序都可以对自己感兴趣的广播进行注 ...
- android 学习随笔十六(广播 )
1.广播接收者 BroadcastReceiver 接收系统发出的广播 现实中的广播:电台为了传达一些消息,而发送的广播,通过广播携带要传达的消息,群众只要买一个收音机,就可以收到广播了 Andro ...
- Android 通过WIFI状态监听广播,判断进入指定wifi范围
原文地址:http://blog.csdn.net/kongxiuqi/article/details/52524500 --------------------------------------- ...
- Android移动应用基础教程【广播机制】
本章目录 一.广播机制的概述 二. 广播接收者 1. 什么是广播接收者 2.广播接收者的创建 三.自定义广播与广播的类型 1.自定义广播 2 .发送求救信号 3 .广播的类型 四.本章小结 一.广播机 ...
最新文章
- c10k问题及其解决方案
- 大学python和vb哪个简单-vb和python哪个速度快
- React Native开发环境搭建记录
- wordpress woodstock主题导入demo xml文件 execution time out
- UVA 1645 - Count(简单DP)
- 线上Go项目的Docker镜像应该怎么构建?
- 爬虫基础之代理的基本原理
- 电脑键盘上的Alt键的作用
- SAP中汇率固定配置和应用分析测试
- 华为产品总监谈投身互联网:要避免与腾讯竞争
- Python:将Word转成HTML
- VIL-100视频车道线实例数据集格式转换
- 盘点国内MCU级RISC-V内核IP厂商
- Android即时通讯--仿QQ即时聊天:(一)初识Socket
- 以太网 VLAN简介、MAC地址表与VLAN的关联内容
- java调用大漠插件
- 线性布局html,线性布局中的layout_weight属性
- android使用自己定义属性AttributeSet
- python血条游戏代码_零基础快速学十二课Python完整游戏代码,使用「格式符%」来处理...
- python做生日祝福短信_祝福短信之整蛊生日祝福语