当前位置:首页 > Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析(1)
要稍微整理一下:
在AMS中,BroadcastReceiver的过滤条件由BroadcastFilter表示,如上图所示,该类继承自IntentFilter。
由于一个BroadcastReceiver可以设置多个过滤条件,故AMS使用ReceiverList来记录一个BroadcastReceiver对应的所有BroadcastFilter。
同时,BroadcastFilter中持有对ReceiverList的引用,用于记录自己属于哪个ReceiverList; ReceiverList中也保存着对IIntentReceiver的引用,用于记录自己对应于哪个BroadcastReceiver。
AMS中利用mRegisterdReceivers这个HashMap,来保存广播对应的ReceiverList,其中的键值就是BroadcastReceiver对应的IIntentReceiver。
同时,AMS中的mReceiverResolver用于保存所有动态注册BroadcastReceiver对应的BroadcastFilter。注意此处的IntentResolver是一个模板类,并不是一个Map类型的数据结构。
这部分流程比较简单,大致如下图所示:
三、sendBroadcast流程分析
分析完广播接收方注册BroadcastReceiver的流程,现在我们来看看广播发送方sendBroadcast的流程。
与registerReceiver一样,Context.java中定义了多个sendBroadcast的接口,但是殊途同归,这些接口最终的流程基本一致。
因此,我们以比较常见的接口入手,看看整个代码的逻辑。
public void sendBroadcast(Intent intent) { ............
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try {
//StrictMode下,对一些Action需要进行检查 intent.prepareToLeaveProcess(this);
//调用AMS中的接口
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId()); } catch(RemoteException e) { .............. } }
ContextImpl中的sendBroadcast函数比较简单,进行一些必要的检查后,直接调用AMS中接口。
我们跟进流程,看看AMS中的broadcastIntent函数:
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) { ..............
synchronized(this) {
//检查Broadcast中Intent携带的信息是否有问题
//例如:Intent中不能携带文件描述符(避免安全隐患) //同时检查Intent的Flag
intent = verifyBroadcastLocked(intent);
final 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; } }
broadcastIntent中对Broadcast对应的Intent进行一些检查后,调用broadcastIntentLocked进行实际的处理。
broadcastIntentLocked函数非常长,大概600行左右吧…….我们只能分段看看它的主要思路。
1 broadcastIntentLocked函数Part I
final int broadcastIntentLocked(.....) { intent = new Intent(intent);
// By default broadcasts do not go to stopped apps. // Android系统对所有app的运行状态进行了跟踪
// 当应用第一次安装但未使用过,或从程序管理器被强行关闭后,将处于停止状态
// 在发送广播时,不管是什么广播类型,系统默认增加了FLAG_EXCLUDE_STOPPED_PACKAGES的flag
// 使得对于所有BroadcastReceiver而言,如果其所在进程的处于停止状态,该BroadcastReceiver将无法接收到广播
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
//大量条件检查,例如:
//有些Action只能由系统来发送;
//有些Action需要发送方申明了对应权限 ...................
//某些Action,例如Package增加或移除、时区或时间改变、清除DNS、代理变化等,需要AMS来处理
//AMS需要根据Action,进行对应的操作 .................. }
这部分的代码较多,主要是针对具体Action的操作,在分析整体流程时,没有必要深究。 当需要研究对应广播引发的操作时,可以再有针对性的研究。
简单地讲,代码主要工作其实只有两个:
1、根据广播对应Intent中的信息,判断发送方是否有发送该广播的权限;
2、针对一些特殊的广播,AMS需要进行一些操作。
2 broadcastIntentLocked函数Part II
..............
// Add to the sticky list if requested. // 处理粘性广播相关的内容 if (sticky) {
//检查是否有发送权限
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED) { .................. }
//粘性广播不能指定接收权限
if (requiredPermissions != null && requiredPermissions.length > 0) { ..............
return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION; }
if (intent.getComponent() != null) { //粘性广播不能指定接收方 ............ }
// We use userId directly here, since the \ // as a separate set of sticky broadcasts.
//当粘性广播是针对特定userId时,判断该粘性广播与系统保存的是否冲突 if (userId != UserHandle.USER_ALL) {
//取出发送给所有user的粘性广播
ArrayMap
ArrayList
int N = list.size(); int i;
for (i=0; i //发送给特定user的粘性广播,与发送给所有user的粘性广播,action一致时,发生冲突 if (intent.filterEquals(list.get(i))) { //抛出异常
共分享92篇相关文档