BroadcastReceiver源码分析
Posted zhenjie_chang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BroadcastReceiver源码分析相关的知识,希望对你有一定的参考价值。
android系统的广播机制是一种基于消息发布和订阅的事件驱动模型,即广播发送者负责发布消息,而接收者需要先订阅消息,才能接收到消息,Android系统中的广播机制是BroadcastReceiver组件。
广播接收者需要首先将自己注册,最终他们是将自己注册到了AMS服务中,当广播发送者发送一个广播的时候,首先发送到AMS服务中,然后由AMS服务发送给对应的接收者。
BroadCastReceiver注册分析
首先我们来分析广播接收者的注册过程。我们知道,在Activity或者Service中注册一个广播接收者调用registerReceiver方法来接收,该方法最终调用ContextImpl方法来实现。
1. ContextImpl.registerReceiver
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler)
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext());
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context)
IIntentReceiver rd = null;
if (receiver != null)
if (mPackageInfo != null && context != null)
if (scheduler == null)
scheduler = mMainThread.getHandler();
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
else
if (scheduler == null)
scheduler = mMainThread.getHandler();
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
try
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
注册的时候registerReceiver方法直接调用了registerReceiverInternal方法来实现具体的逻辑,这个方法比较简单,mPackageInfo是一个LoadedApk类型的对象,如果mPackageInfo不为空,则根据receiver到LoadedApk中查找这个Receiver关联的Binder对象,如果mPackageInfo为空着直接创建一个Binder对象和receiver关联起来。最后通过进程间请求,调用AMS服务的registerReceiver方法来注册该广播
我们知道LoadedApk这个类代表了一个加载到内存中的应用程序,它里边保存了一个应用程序的基本资源信息,包括ActivityThread,ApplicationInfo,包名,资源路径,Lib路径等信息,它同时还保存了所有注册的广播的信息。
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
这个mReceivers中以Context为键值保存了一个Map类型的信息ArrayMap<BroadcastReceiver, ReceiverDispatcher>,一个Context代表了一个Activity或者一个Service,因为Activity和Service最终都实现了Context。以Context为键值查找的时候可以直接找到某个Activity或者Service中注册的所有广播接收者。
接着看ArrayMap<BroadcastReceiver, ReceiverDispatcher>,这个是以BroadCastReceiver为键值保存了一个ReceiverDispatcher的值,主要是为了将BroadcastReceiver和一个ReceiverDispatcher关联起来。
首先我们来看下LoadedApk.ReceiverDispatcher这个类具体是什么作用。
static final class ReceiverDispatcher
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;
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser)
LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
……
//一个Binder类型的对象,实现了Binder的服务端
final IIntentReceiver.Stub mIIntentReceiver;
//指向一个BoradCastReceiver
final BroadcastReceiver mReceiver;
//指向发送者的Context上下文信息
final Context mContext;
//指向发送者进程主线程的Hander对象,通过它可以向主线程发送消息
final Handler mActivityThread;
final Instrumentation mInstrumentation;
……
Receiver这个类中保存了一些变量,分别指向了发送者的一些信息,其中InnerReceiver类型的变量最重要,它实现了IIntentReceiver.Stub,是一个Binder类型的本地对象,属于Binder服务端。
这个类的主要作用就是,把Binder对象通过进程间传递出去,其他进程要和这边的Receiver通信的时候,直接调用该Binder的代理端,这边的InnerReceiver就可以接收到消息,然后在通过主线程的Handler对象发送个主线程来处理这个消息。
接着看LoadedAPk.getReceiverDispatcher方法的实现。
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered)
synchronized (mReceivers)
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
if (registered)
//根据当前的Context,查找关于这个Activity或者Service注册的所有广播接收者信息
map = mReceivers.get(context);
if (map != null)
//找到这个广播接收者关联的一个Binder服务端对象
rd = map.get(r);
if (rd == null)
//如果LoadedApk中没有保存的话,就先创建一个,然后保存的LoadedAPk的mReceivers集合中
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered)
if (map == null)
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
map.put(r, rd);
else
rd.validate(context, handler);
rd.mForgotten = false;
return rd.getIIntentReceiver();
根据前面方法的分析看出,这个方法的作用是用来在LoadedApk中查找这个广播接收者关联的Binder对象的。如果有则直接返回,如果找不到则new一个ReceiverDispatcher对象,保存到mReceivers集合中,然后再返回Receiver关联的Binder对象。
2. AMS.registerReceiver
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId)
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
int callingUid;
int callingPid;
synchronized(this)
if (caller != null)
//获取调用注册程序的进程信息和Uid,Pid
callerApp = getRecordForAppLocked(caller);
……
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
userId = handleIncomingUser(callingPid, callingUid, userId,
true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
Iterator<String> actions = filter.actionsIterator();
……
int[] userIds = UserHandle.USER_ALL, UserHandle.getUserId(callingUid) ;
//查询所有的Action相关的粘性广播,并保存到stickyIntents中
while (actions.hasNext())
String action = actions.next();
for (int id : userIds)
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null)
ArrayList<Intent> intents = stickies.get(action);
if (intents != null)
if (stickyIntents == null)
stickyIntents = new ArrayList<Intent>();
stickyIntents.addAll(intents);
……
synchronized (this)
//根据注册进程传入的binder对象从mRegisteredReceivers查找对应的ReceiverList
//mRegisteredReceivers列表,以注册进程传入的receiver.asBinder为key,保存了ReceiverList
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
//如果第一次调用则new一个ReceiverList,并添加到mRegisteredReceivers列表中
if (rl == null)
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null)
rl.app.receivers.add(rl);
else
try
receiver.asBinder().linkToDeath(rl, 0);
catch (RemoteException e)
return sticky;
rl.linkedToDeath = true;
mRegisteredReceivers.put(receiver.asBinder(), rl);
……
//创建一个BroadCastReceiver对象并保存到ReceiverList中,BroadcastFilter表示广播的过滤条件,一个广播接收者可以有多个过滤条件
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
//同时也保存到mReceiverResolve列表中,这个列表保存了系统中所有动态注册的广播接收者所对应的BroadCastFilter
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null)
……
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++)
//遍历所有符合条件的stick广播Intent,然后把它封装成一个BroadcastRecord并添加到BroadcastQueue的ParallelBroadcastLocked列表
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
return sticky;
该方法的主要工作:
1. 获取调用程序的进程信息ProcessRecord及PID和Uid
2. 从AMS的mStickyBroadcasts列表中查询所有和action匹配的sticky广播,并保存到allSticky列表中,该Action就是即将要注册的广播的Action。
3. 我们知道参数receiver是一个IIntentReceiver类型的变量,是binder代理端,指向了应用进程要注册的广播Receiver,registerdReceivers是一个map类型的变量,它以receiver.asBInder为key保存了ReceiverList。这样应用进程的一个Receiver就和AMS服务中的一个ReceiverList关联起来了。
ReceiverList又是什么呢? 它继承自ArrayList<BroadCastFilter>,同时它内部有一个IntentReceiver类型的变量指向一个Receiver,这样一个Receiver就和多个BroadcastFilter关联起来了。因为一个广播可以有多个过滤条件。
第三步的主要工作就是从mRegisterReceivers中查找receiver对应的ReceiverList信息,如果为空,就表示该广播没有注册,需要新创建一个ReceiverList并添加到mRegisterReceivers列表中。
4. 创建一个BroadcastFilter,添加到上面的ReceiverList中,同时也添加到mReceiverResolver集合中,mReceiverResolver集合存储了所有动态注册的广播的过滤条件BroadcastFilter,便于广播发送的时候查找Receiver。
5. 将第二步找到的和此接收者Action相关的sticky广播遍历,封装成一个BroadcastRecord,加入队列准备发送。
3. BroadCastQueue. scheduleBroadcastsLocked
public void scheduleBroadcastsLocked()
//发送Handler消息个AMS服务,通知AMS服务发送广播
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
最终调用scheduleBroadcastsLocked方法,发送一个Hander消息给AMS服务,由AMS服务来发送广播。
同时也了解了sticky广播的实现原理,发送完的sticky广播会保存到mStickyBraodcasts集合中,在注册的时候根据action查找对应的sticky广播,如有有相关的sticky广播的话,注册完成后,立刻发送出去,这样刚注册的Receiver就可以立刻受到sticky的广播了
这样广播的动态注册就完成了。经过分析可以看出,AMS服务是广播的注册中心,广播接收者要想接收广播消息需要将自己注册到AMS服务中去,指定要接收的广播类型,发送广播也是首先发送给AMS服务,由AMS服务来找到对应的广播接收者,然后在调用对应的广播接收者来处理。
Broadcast的发送过程分析
广播的发送方式分为有序广播和无序广播,在注册广播的时候可以指定广播的优先级,这样广播就会优先发送给优先级比较高的广播,然后才会发送给优先级比较低的广播。而无序广播则不管广播接收者的优先级。
同样要分析广播的发送过程需要从ContextImpl类开始分析,因为它最终实现了sendBroadcast的接口和方法,service和activity发送广播的过程都是调用ContextImpl的方法来实现的。
1. ContextImpl.sendBroadcast
public void sendBroadcast(Intent intent)
……
try
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
ContextImpl的发送广播的方法最终通过进程间通信请求,调用AMS服务的broadcastIntent方法。同时在参数中将发送者的ApplicationThread和广播的intent传递给了AMS服务。
这个方法比较简单,进程间通信方式不再解释,我们直接看AMS服务的broadcastIntent方法
2. AMS.broascastIntent
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle options,
boolean serialized, boolean sticky, int userId)
……
synchronized(this)
//验证Intent信息
intent = verifyBroadcastLocked(intent);
//获取调用进程的信息ProcessRecord
final ProcessRecord callerApp = getRecordForAppLocked(caller);
//获取调用应用的uid和pid
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
//通过broadcastIntentLocked方法来继续处理发送广播的逻辑
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, null, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
这个方式实现也是比较简单,获取调用进程的ProcessRecord信息和调用程序的UID和PID,然后直接调用broadcastIntentLocked方法来继续处理发送广播的逻辑
3. AMS.broadcastIntentLocked
这个方法比较长,我们一部分一部分的来看
intent = new Intent(intent);
//确保要接收的广播的用户仍然存在,否则的话直接返回错误信息
if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false))
if ((callingUid != Process.SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction()))
Slog.w(TAG, "Skipping broadcast of " + intent
+ ": user " + userId + " is stopped");
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
//如果当前的广播类型是sticky广播,则把广播添加到List列表中去
if (sticky)
//首先检查相关的权限信息
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED)
String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
+ callingPid + ", uid=" + callingUid
+ " requires " + android.Manifest.permission.BROADCAST_STICKY;
……
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");
//找到和这个用户相关的所有的sticky广播的信息
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
if (stickies == null)
stickies = new ArrayMap<>();
mStickyBroadcasts.put(userId, stickies);
//从得到的广播信息中,找到和当前要发送广播action相同的sticky广播
ArrayList<Intent> list = stickies.get(intent.getAction());
if (list == null)
list = new ArrayList<>();
stickies.put(intent.getAction(), list);
//遍历拿到的stick广播信息,如果已经存在,则更新替换掉他
//如果不存在,则添加进去
final int stickiesCount = list.size();
int i;
for (i = 0; i < stickiesCount; i++)
if (intent.filterEquals(list.get(i)))
// This sticky already exists, replace it.
list.set(i, new Intent(intent));
break;
if (i >= stickiesCount)
list.add(new Intent(intent));
参数sticky用来描述要发送的广播是否是一个sticky类型的广播,如果是,就需要对sticky广播进行一些处理,在前面我们分析过,在注册广播的时候,如果AMS中保存了对应action类型的sticky广播,则直接将保存的sticky广播发送出去。那些sticky广播是如何保存到AMS服务中去的呢?
代码逻辑就是在这边处理的,在注册一个广播的时候,如果它是sticky类型的广播,就直接将它保存在AMS服务的mStickyBroadcasts集合中。
首先从AMS系统服务中查询Action相关的sticky广播,然后遍历,如果该sticky广播已经在AMS中注册保存了,则用新的Sticky广播替换掉它,否则直接添加到列表中去。
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
//如果没有FLAG_RECEIVER_REGISTERED_ONLY标签,说明不是发送给动态注册的广播的,那么调用collectReceiverComponents,查找所有和该Intent相关的广播接收者
//保存到receivers列表中
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0)
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
//如果没有指定特定的广播接收者,则查找所用的动态注册的广播接收者的信息
if (intent.getComponent() == null)
……
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
第二部分的主要处理逻辑就是查找和intent相关的广播接收者。我们知道广播注册的时候有两种注册方法,一种是静态注册方法,在Manifest文件中注册,另一种是动态注册方法,在代码中调用registerBroadcast方法来注册广播。
这个地方进行了两次查找,判断发送广播的intent的标签,如果不是仅仅发送给动态注册的广播的时候,就调用collectReceiverComponents方法查询所有Intent感兴趣的广播接收者,这里仅仅去PMS服务中查询了感兴趣的静态注册的广播接收者,保存在receivers列表中。
然后判断这个广播是否指定了值发送给特定的广播接收者,如果没有指定的话,默认发送给所有感兴趣的广播接收者,这时候再去查询一次,这次查询的是所有对这个Intent感兴趣的动态注册的广播接收者,保存在registeredReceivers列表中。
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0)
//如果当前不是有序广播,且动态注册的感兴趣的广播不为0,将广播intent封装为一个BroadcastRecord
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);
……
//发送该广播给动态注册的广播接收者
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
//发送完成后,将registeredReceivers 置为 null
registeredReceivers = null;
NR = 0;
第三部分的处理工作:
如果当前发送的广播不是有序广播,且动态注册的广播接收者>0,此时则需要把这个广播优先发送给动态注册的广播接收者,为什么不和静态注册的广播一起发送,却要分开发送呢?这样做是因为,动态注册的广播接收者一般情况下都已经运行起来了,而静态注册的广播接收者只是在Manifest文件中注册,要想接收广播必须要等待要接收的组件启动起来才可,所以这边处理选择分开发送,优先发送动态注册的广播接收者。
发送的广播的时候,无序广播都添加到了mParallelBroadcats队列中,具体发送的处理逻辑后边在详细分析。
下一步处理发送给静态注册的广播接收者的逻辑。
int ir = 0;
//感兴趣的静态注册广播接收者不为空
if (receivers != null)
//首先对于特殊的ACTION_PACKAGE_ADDED的广播需要特殊处理
//这样是为了防止一些应用则安装完成之后监听到package_add的广播就立刻运行
//所以要把它已标记起来,以后从广播接收者信息中移除
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 ;
//同样,如果Intenty有ACTION_EXTERNAL_APPLICATIONS_AVAILABLE标签,则同样移除
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--;
处理静态注册的广播接收者逻辑的时候,首先判断该广播事件是否是Package_add的广播事件,如果是的话,需要从广播接收者列表中移除,这样为了防止一些毒瘤应用在安装完成后接收到package_add广播就立刻运行。
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
//上面的无序广播处理后,把mRegisterReceivers列表置为null,并且把NR设置成了0
//而此时要想满足此处条件,NR不为0,说明此处是有序广播的处理逻辑
//遍历动态注册的广播和静态注册的广播,安priority值排序合并放到receivers列表中
while (it < NT && ir < NR)
if (curt == null)
curt = (ResolveInfo)receivers.get(it);
if (curr == null)
curr = registeredReceivers.get(ir);
if (curr.getPriority() >= curt.priority)
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
else
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
……
//有序广播接收者不为空,发送有序广播
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();
此处处理逻辑主要为有序广播的处理逻辑。
在前面的分析中知道,如果是无序广播的时候,动态注册的广播接收者就直接处理了,然后把mRegisteredReceivers列表置为了null,所以在有序广播的时候就不会有动态注册的广播接收者合并进来。此时就会把静态广播放到有序广播类别mOrderedBroadcasts队列中发送。为什么会把静态注册的广播接收和按照有广播的逻辑来发送呢?有序广播发送的处理逻辑是发送完成一个在发送另一个,而无序广播是直接循环发送出去,静态注册的广播接收者的进程不一定已经运行起来了,在发送的过程中可能需要先启动进程,这样很消耗资源,所以一个一个发送会好一点。
如果发送的是有序广播,动态注册的广播在前面逻辑中则不会处理,此处就会把动态注册的广播和静态注册的广播按照priority值排序,然后合并到receivers列表中。
最终把receiver列表中的广播接收者封装成一个BroadcastRecord对象放到mOrderedBroadcasts队列中进行发送。
此处逻辑可以总结出来,如果是无序广播的话。动态注册的广播接收者会比静态注册的广播者优先收到广播
下面我们来分析具体的广播发送处理逻辑:
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);
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
对于BroadCast的发送的处理逻辑大体上分为以上几个步骤
1. 根据广播的Intent找到对应的广播队列
2. 将Intent和广播接收者封装成一个BroadcastRecord对象
3. 将BroadcastRecord对象添加拿到queue队列中
4. 调用queue的scheduleBroadcastsLocked方法发送广播
4. AMS. broadcastQueueForIntent
BroadcastQueue broadcastQueueForIntent(Intent intent)
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
这个方法很简单,就是根据Intent中是否有Flag_Receiver_foreground标签,来决定添加到那个队列中去。
这个标签是什么作用呢?看下SDK中的描述:
FLAG_RECEIVER_FOREGROUND 接受器以前台优先级运行
If set, when sending a broadcast the recipient is allowed to run at foreground priority, with a shorter timeout interval. During normal broadcasts the receivers are not automatically hoisted out of the background priority class.
广播默认都是后台优先级,如果广播中添加了这个标签就会以前台优先级运行,发送到接收到广播的时间间隔会更短。
也就是说,默认广播都是天剑到mBgBroadcastQueue队列中去的,只有广播添加了这个标签后,才会被添加到mFgBroadcastQueue队列中。这两个队列中处理逻辑的区别我们后面再详细分析。
第二步,将Intent和广播接收者队列封装成了一个BroadcastRecord对象,也就是说每一个广播在AMS都是对应一个BroadcastRecord对象的。
5. BroadcastQueue. Enqueue……BroadcastLocked
BroadcastQueue中有两个添加队列的方法:
public void enqueueParallelBroadcastLocked(BroadcastRecord r)
mParallelBroadcasts.add(r);
r.enqueueClockTime = System.currentTimeMillis();
public void enqueueOrderedBroadcastLocked(BroadcastRecord r)
mOrderedBroadcasts.add(r);
r.enqueueClockTime = System.currentTimeMillis();
两个方法处理逻辑基本相同,一个是将广播放到mParallelBroadcasts队列中,一个是放到mOrderedBroadcasts队列中,者两个列表有什么区别呢?
mParallelBroadcasts列表中保存的都是动态注册的广播接收者信息,它里面的广播处理的时候是循环发送的
mOrderedBroadcasts列中中主要保存了静态广播接收者和有序广播接收者的信息,它里边的广播处理的时候是一个发送完成之后才会发送第二个的。
6. BroadcastQueue. scheduleBroadcastsLocked
public void scheduleBroadcastsLocked()
……
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
该消息发送了了一个Handler消息,有Hander的handleMessage来处理,最终调用了processNextBroadcast方法来处理。
7. BroadcastQueue. processNextBroadcast
动态注册广播接收者的处理逻辑
这个方法有点复杂,主要是两种广播的处理逻辑不一样,那我们就先分析动态注册的广播接收者处理逻辑,然后在分析静态注册或者有序广播的处理逻辑。
我们在前面讲过,动态注册的广播接收者处理的时候是一次性的循环发送完国有广播,而静态注册或者有序广播则是一个发送完成在发送另一个。
final void processNextBroadcast(boolean fromMsg)
synchronized(mService)
BroadcastRecord r;
……
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);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
addBroadcastToHistoryLocked(r);
动态广播的处理逻辑是在processNextBroadcast方法的开始处理的,变量mParallelBroadcasts列表,从列表开始位置一次拿到保存的BroadcastRecord对象,每个BroadcastRecord中可能对应多个广播接收者,依次遍历广播接收者,然后调用deliverToRegisteredReceiverLocked方法发送广播
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered)
boolean skip = false;
//检查是否具有相关权限,没有权限则skip = true 表示跳过这个广播接收者
……
//检查接收广播的进程是否存在,如果接收广播的进程不存在则跳过这个广播接收者,不向它发送该广播
if (filter.receiverList.app == null || filter.receiverList.app.crashing)
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
+ " to " + filter.receiverList + ": process crashing");
skip = true;
if (!skip)
if (ordered)
//有序广播处理逻辑
try
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
……
我们只关心和我们处理逻辑相关的关键代码。
检查相关的权限,如果没有权限则skip设置为true,
检查广播接收者对应的进程是否存在,如果不存在skip设置为true
我们目前是无序广播,所以ordered为false,最终调用performReceiveLocked方法来处理。
我么回忆下ReceiverList是什么,ReceiverList是我们在注册广播接收者的时候,一个广播接收者的代表信息,它继承者ArrayList<BroadcastFilter>,这样可以实现一对多的数据结构,因为一个广播接收者可能对应多个BroadcastFilter。所以receiverList.app代表了广播接收者所在的进程,receiverList.receiver则是一个Binder对象,指向了App进程中的一个InnerReceiver的对象。
继续看performReceiveLocked方法如何处理。
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException
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);
参数receiver是一个IIntentReceiver接口的Binder代理对象,它的服务端是应用进程的InnerReceiver,参数app则是接收广播的应用进程的进程对象ProcessRecord,在注册广播接收者的时候,我们知道在创建广播接收者对应的ReceiverList对象的时候会初始化app的值。
ReceiverList rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver);
一般情况下receiver的变量app都是不为null的,所以大部分情况下广播都是通过应用进程的ApplicationThread对象的Binder代理对象,通过进程间调用,将这个广播发送到应用进程主线程来处理,应用进程的主线程再调用对应的receiver来处理。而不是直接通过Receiver的Binder代理端来进行进程间的调用。
接着看ApplicationThread的scheduleRegisteredReceiver方法。
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);
此时直接调用receiver的performReceive方法,receiver是一个binder类型的代理对象,这个方法调用最终调用到InnerReceiver的performReceiver方法中。
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser)
//得到当前的dispatcher
LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
if (rd != null)
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
……
这个方法很简单,首先得到对应的dispatcher,然后调用dispatcher的方法来处理。
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser)
Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (!mActivityThread.post(args))
if (mRegistered && ordered)
IActivityManager mgr = ActivityManagerNative.getDefault();
args.sendFinished(mgr);
mActivityThread是一个Handler对象,该对象指向ActivityThread的Hander对象,Handler的post最终调用了Args的run方法。
final class Args extends BroadcastReceiver.PendingResult implements Runnable
……
public void run()
final BroadcastReceiver receiver = mReceiver;
……
try
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
……
变量receiver指向了一个广播接收者BroadcastReceiver,然后调用receiver.onReceive()方法。到此动态注册的广播接收者接收广播的逻辑就处理完成了。
下一步看静态注册或者有序广播的发送逻辑。
静态注册或者有序广播接收者的处理逻辑
我们再回到processNextBroadcast方法中,接着往下看
//判断当前是否正在等待静态注册的广播接收者控件启动并接收任务
if (mPendingBroadcast != null)
boolean isDead;
synchronized (mService.mPidsSelfLocked)
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
if (!isDead)
//如果有个任务正在启动则直接返回继续等待
return;
boolean looped = false;
do
//从mOrderedBroadcasts列表中获取下一个要发送的广播
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();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers)))
broadcastTimeoutLocked(false); //强制停止这个广播
forceReceive = true;
r.state = BroadcastRecord.IDLE;
……
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive)
//如果这个广播接收者receivers已经处理完了,或者这个广播已经强制停止了
if (r.resultTo != null)
……
//从Handler延时消息中移除timeout的消息
cancelBroadcastTimeoutLocked();
//添加到history中
addBroadcastToHistoryLocked(r);
//从mOrderedBroadcasts列表中移除
mOrderedBroadcasts.remove(0);
//置为null
r = null;
looped = true;
continue;
while (r == null);
第一步:判断mPendingBroadcast是不是为空,这个mPendingBroadcast的作用是什么呢?
我们知道,mOrderedBroadcast中保存的广播接收者都是需要一个发送完成另一个才可以发送的。其中静态注册的广播接收者有可能对应的进程还未启动,者就需要首先来启动该进程。如果是这种情况的话mPendingBroadcast就是描述了一个正在等待静态注册的对应进程启动的广播。
如果他不为空,说明当前有任务正在处理,在对这个正在启动的进程检查之后就直接退出了。
如果mPendingBroadcast不为空,AMS就会继续等待,否则的话就会准备处理mOrderedBroadcast的下一个广播。
接下来是一个while循化,主要工作是从mOrderedBroadcasts列表中找到下一个要处理的广播,退出条件是 r != null,即找到下一个合适的广播。
1.首先从列表的开始拿出第一个BroadcastRecord,第一个有可能是正在处理的广播,判断他是否已经开始分发广播,如果开始但是已经超时,则直接强制停止这个广播。
2.然后,判断这个广播是否已经处理完成,或者是否强制停止,然后直接停止该广播。
3.将发送给AMS的关于超时的延时消息移除
4.将第一个广播移除掉,并把r设置为null,表示没有找到合适的广播,然后继续循环查找,知道找到下一个合适的广播,退出循环。
//获取下一个要执行的Receiver信息
int recIdx = r.nextReceiver++;
r.receiverTime = SystemClock.uptimeMillis();
//如果recIdx = 0,说明这个广播的才刚开始发送广播任务,则记录下当前的时间为分发广播的时间
if (recIdx == 0)
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
if (! mPendingBroadcastTimeoutMessage)
//向AMS的handler发送一个延时的超时消息,到超时的时间后自动发送
long timeoutTime = r.receiverTime + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
……
final Object nextReceiver = r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter)
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
deliverToRegisteredReceiverLocked(r, filter, r.ordered);
return;
我们知道一个广播可能对应多个广播接收者,在mOrderedBroadcasts列表处理的时候是需要向一个Receiver发送成功后,在向另一个发送。BroadcastRecord的变量nextReceiver表示下一个广播接收者的序号。
如果为0,说明这个广播的广播接收者
以上是关于BroadcastReceiver源码分析的主要内容,如果未能解决你的问题,请参考以下文章