前言:知其所以,知其所以然。
广播使用的三步骤:
- 注册广播
- 发送广播
- 接收广播
本文主要谈一下《注册广播》,务必谈得深入,谈得彻底,解大家心中的一个闷。
下面是开发者执行registerReceiver(...) binder调用到system_server进程中AMS的调用过程。虽然核心的代码是在AMS中执行的,但是之前的步骤也是很重要的。下面按照执行步骤详细讲解一下每一步发生了什么情况?
1.ContextImpl -> registerReceiverInternal(...)
这是执行的第一个重要步骤。这个函数从代码来看分为上下两部分:
- 获取一个IIntentReceiver binder对象
- Binder调用AMS中registerReceiver(...)函数
1.1 获取一个IIntentReceiver binder对象
我们先看一下这个获取IIntentReceiver binder对象的代码:
ContextImpl.java
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mLoadedApk != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mLoadedApk.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
}
这段代码表示:如果当前的mLoadedApk和context不为空,那么直接从mLoadedApk对象中取对应的IIntentReceiver对象,如果条件不成立的话,需要重新new一个LoadedApk.ReceiverDispatcher对象,然后从中取出IIntentReceiver对象。
看到这里,我想提出三个疑问:
- LoadedApk类中是做什么的?
- IIntentReceiver是什么对象?
- LoadApk初始化什么时候?
1.1.1 解析LoadedApk类
这是LoadedApk 核心类的图示。LoadedApk这个类就是保存当前加载好的apk的本地状态,给大家看一下LoadedApk中的基本属性:
private final ActivityThread mActivityThread;
final String mPackageName;
private ApplicationInfo mApplicationInfo;
private String mAppDir;
private String mResDir;
private String[] mOverlayDirs;
private String mDataDir;
private String mLibDir;
private File mDataDirFile;
private File mDeviceProtectedDataDirFile;
private File mCredentialProtectedDataDirFile;
private final ClassLoader mBaseClassLoader;
private final boolean mSecurityViolation;
private final boolean mIncludeCode;
private final boolean mRegisterPackage;
private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
/** WARNING: This may change. Don't hold external references to it. */
Resources mResources;
private ClassLoader mClassLoader;
private Application mApplication;
private String[] mSplitNames;
private String[] mSplitAppDirs;
private String[] mSplitResDirs;
private String[] mSplitClassLoaderNames;
private final ArrayMap> mReceivers
= new ArrayMap<>();
private final ArrayMap> mUnregisteredReceivers
= new ArrayMap<>();
private final ArrayMap> mServices
= new ArrayMap<>();
private final ArrayMap> mUnboundServices
= new ArrayMap<>();
上面都是当前加载的apk的基本属性:
- apk的data目录,lib目录,或者其他的基本存储目录等等
- apk相关的属性:包名、applicationInfo等等
- apk包含的receiver和service,以及已经取消注册的receiver和解绑的service
- 如果当前的apk有分包的话,还有apk的分包目录和分包加载的classloader名称,等等。
上面这些信息,就是向我们透露一件事情,就是LoadedApk中包含这加载的apk的基本属性。那么也就不难理解,我们的receiver肯定也需要在这里备份一份了。
- ReceiverDispatcher:
显然是处理当前apk中包含的receiver的,具体怎么处理,下面会讲解。- ServiceDispatcher:
对应的,这是处理当前apk中包含的service的。将在service章节中涉及到这个,这儿不展开讲解了。
1.1.2 解析IIntentReceiver
从上面的LoadedApk类的结构图来看,我们知道InnerReceiver是继承IIntentReceiver.Stub,说明这是一个binder结构:
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
//......
}
}
从上面的代码中看出了两点:
- InnerReceiver中持有ReceiverDispatcher 应用,且有强引用和弱引用两种,但是在核心函数performReceive中用到是弱引用。这儿的调用等会讲解完AMS中的registerReceiver之后,自然会明白。
- 我们之前在传入AMS的registerReceiver时,传入了就是InnerReceiver的实例,很显然,之后在AMS中肯定会回调到performReceiver,现在看起来是不是有点理解了。
1.1.3 LoadedApk 初始化
上面也有说道一个条件: mLoadedApk != null && context != null ,这个mLoadedApk就是LoadedApk的实例,上面我们说了LoadedApk中存放着当前加载的apk的基本信息,既然是基本信息,那么这个类十分重要,那他加载的时候肯定很靠前。
下面简单叙述一下mLoadedApk的过程:
- ActivityThread -> attach(...) ,非系统进程情况下,执行:
context.mLoadedApk.makeApplication(...),这儿的mLoadedApk是系统进程的,不是我们当前的应用进程的。- LoadedApk -> makeApplication(...) ,执行的函数是ContextImpl.makeApplication(mActivityThread, this),这儿传入的this就是当前的LoadedApk的实例。
- 传入到ContextImpl中,就让当前的应用进程中存在了一个,mLoadedApk实例了。
1.1.4 LoadedApk.ReceiverDispatcher构造函数
介介绍完背景知识,我们开始从上面的调用开始,了解一下调用的整个过程到底干了什么?
当前的 mLoadedApk != null && context != null 如果不满足的话,说明当前的mLoadedApk为null,也就是还没有初始化,这儿执行分为两步:
- 执行LoadedApk.ReceiverDispatcher构造函数
- 从上面构建出来的对象中调用getIIntentReceiver()获取IIntentReceiver.Stub binder对象。
1.1.4.1 执行LoadedApk.ReceiverDispatcher构造函数
构造函数的代码如下:
ReceiverDispatcher(BroadcastReceiver receiver, Context context,
Handler activityThread, Instrumentation instrumentation,
boolean registered) {
if (activityThread == null) {
throw new NullPointerException("Handler must not be null");
}
mIIntentReceiver = new InnerReceiver(this, !registered);
mReceiver = receiver;
mContext = context;
mActivityThread = activityThread;
mInstrumentation = instrumentation;
mRegistered = registered;
mLocation = new IntentReceiverLeaked(null);
mLocation.fillInStackTrace();
}
构造函数中传入的参数有5个:
- receiver:
就是当前需要注册的receiver,是我们定义的。- context:
这个context是getOuterContext(),这个就是执行setOuterContext(context)时传入的context,执行这个函数的地方在ActivityThread->performLaunchActivity(...)时执行,就是当前的activity的context- activityThread:
这是一个Handler对象,从赋值来看:
mMainHandler.getHandler() ,是当前activity的主线的handler- instrumentation:
当前传入的是null- registered:
注册的时候传入的参数是true,如果反注册的时候传入的参数是false
除了传入上面的几个参数,其他的都是赋值操作,其中还有一个构造方法:
mIIntentReceiver = new InnerReceiver(this, !registered);
这个InnerReceiver是ReceiverDispatcher的内部类,这个内部类继承了IIntentReceiver.Stub ,本质上是一个binder对象。
1.1.4.2 获取IIntentReceiver.Stub binder对象
上面说过了在执行LoadedApk.ReceiverDispatcher构造函数的时候构造了IIntentReceiver.Stub对象,就是mIIntentReceiver实例,然后赋值。
1.1.5 获取IIntentReceiver对象
之前 《1.1.4 LoadedApk.ReceiverDispatcher构造函数》谈得前提条件是
mLoadedApk != null && context != null 不成立,如果这个条件成立了,那么当前的IIntentReceiver对象要从什么地方取?
rd = mLoadedApk.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
当前的mLoadedApk不为空,通过getReceiverDispatcher获取想要的对象。
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
rd = map.get(r);
}
}
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
mReceivers就是
private final ArrayMap> mReceivers
= new ArrayMap<>();
ArrayMap 的key就是当前的context,上面也介绍了一下context的来历,value就是我们定义的一个ArrayMap
值得注意的是:rd.validate(context, handler);到底想说什么?
void validate(Context context, Handler activityThread) {
if (mContext != context) {
throw new IllegalStateException(
"Receiver " + mReceiver +
" registered with differing Context (was " +
mContext + " now " + context + ")");
}
if (mActivityThread != activityThread) {
throw new IllegalStateException(
"Receiver " + mReceiver +
" registered with differing handler (was " +
mActivityThread + " now " + activityThread + ")");
}
}
校验一下当前的context和activityThread,如果不是同一个activity,那当前创建的的LoadedApk.ReceiverDispatcher对象是不合法的,会直接抛出异常。
1.2 Binder调用AMS中registerReceiver(...)函数
Android 8.0 代码和之前已经有了很大的变化,主要的架构没有变化,但是在AMS和AMP之间调用的部分有了一些变化,现在已经找不到AMP代码部分了,之前通过AMPbinder调用到AMS,现在直接通过ActivityManager.getService()直接调用到AMS
ActivityManager.java
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
通过这样的方式获取ACTIVITY_SERVICE的binder,底层有很复杂的binder通信的过程,最后会在AMS中接收到registerReceiver(...)中。
2.AMS-registerReceiver剖析
到这儿来算是走到了system_server进程了,注册receiver,绑定的service等等都会system_server进程中完成,这儿会统一管理这些组件,所以核心的逻辑也会在这里面。
2.1 registerReceiver参数解析
registerReceiver中传入了7个参数:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags)
调用的地方:
final Intent intent = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
broadcastPermission, userId, flags);
- caller:
传入IApplicationThread binder对象,通过这个对象可以找到当前调用的Process- callerPackage:
当前调用者的package 名称- receiver:
这个参数很关键,就是我们上面一直在讲的IIntentReceiver对象,这个对象的在后面有大用处,别忘了还有一个performReceive等着回调了。- filter:
当前注册的receiver的action属性或者其他的属性就放在这里。- permission:
广播设置的属性- userId:
调用的userId标识- flags:
这个flags标识,默认情况下是0
2.2 获取uid与pid
if (caller != null) {
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
上面这段代码总结起来:
- 当前调用者caller binder结构存在,根据caller取出对应的ProcessRecord,如果当前ProcessRecord为空,说明注册广播的进程不存在,抛出异常。
- 当前ProcessRecord不为空,就要判断当前的调用者时候和传入的pkg属性匹配,如果不匹配,当前的调用不合法,要抛出异常。如果匹配的话,当前PorcessRecord会复制uid和pid。
- 当前调用者caller binder结构不存在,当前调用pkg也是不存在的,则从Binder底层赋 uid和pid。
总之一句话,就是当前进程要存在且合法。AMS中到处都有这样的校验过程。
2.3 获取当前的粘性广播
要了解粘性广播的话,就知道,所谓粘性广播,如果当前发送的粘性广播在注册广播之前,这个广播也是会被收到的;繁殖不是粘性广播,就收不到。
开始根据上面生成的uid获取特定用户的粘性广播:
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
ArrayMap> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
ArrayList intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList();
}
stickyIntents.addAll(intents);
}
}
}
}
这儿得到stickyIntents 后面还会用到。如果当前receiver的intentFilter匹配粘性广播序列中任何一个intent的话,讲当前的intent加载到allSticky数组中。这个数组也会有用处。
Intent sticky = allSticky != null ? allSticky.get(0) : null;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
if (receiver == null) {
return sticky;
}
这儿表示当前即使当前的receiver为空,粘性广播序列也会返回,切返回的是数组中第一个索引的值。这儿恰好能说明粘性广播的特性。
2.4 注册当前的广播
匹配玩粘性广播,这儿要操作普通的广播了。
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
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);
}
//......
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
rl.add(bf);
mReceiverResolver.addFilter(bf);
这里的是一个mRegisteredReceivers是一个HashMap,他的key是一个binder结构,key是一个ReceiverList
final HashMap mRegisteredReceivers = new HashMap<>();
这个对应关系很好理解,binder对象中有receiver的处理的地方,一对多的关系,这个ReceiverList可以包含多个Receiver,接下来广播需要入队了。
2.5 广播入队列
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
- AMS中有两种广播队列,前台广播队列和后台广播队列,一般情况下都是后台广播队列。前台广播队列除非手动指定。
- BroadcastRecord 是描述广播的数据结构,这里描述了一系列广播的属性。
- queue.enqueueParallelBroadcastLocked(r); 当前receiver入队,广播还分为两种:并行广播和有序广播,并行广播实际上就是无序广播。
- queue.scheduleBroadcastsLocked(); 这个调用要展开说下,见下文。
queue.scheduleBroadcastsLocked()通过sendMessage(BROADCAST_INTENT_MSG)调用到processNextBroadcast(true),这个函数代码非常庞大,有600行。我们挑关键点讲解。
2.5.1 处理并行广播
记住这儿执行的一个关键的函数:
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
boolean skip = false;
//中间省略了很多代码,主要的执行目的是check一下当前receiver的权限是否符合要求。
//如果符合要求,那么skip为false,不符合要求。skip为true
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(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) {
Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
if (ordered) {
r.receiver = null;
r.curFilter = null;
filter.receiverList.curBroadcast = null;
if (filter.receiverList.app != null) {
filter.receiverList.app.curReceivers.remove(r);
}
}
}
}
下面这个函数会直接回调performReceive,结尾处会讲解一下这个performReceive做什么工作。
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
2.5.2 处理当前挂起的广播
这要看当前的进程是否存在或者crash,如果不存在或者crash,那么当前的进程死掉了,直接return,不继续往下走了。
2.5.3 处理有序广播
处理有序广播要关注一下注册时间,这是判断有序的依据,了解这个之后,它的处理逻辑还是放在performReceiveLocked(...)中。
2.5.4 处理下一个广播
之前的处理并行广播、挂起广播、有序广播都是处理当前存在queue中的广播,处理完了之后,开始处理这之后的广播,就是俗称的下一个广播。
2.5.5 判断广播调起的一系列权限问题
如果开始的时候申请了一些权限,这儿需要对权限进行校验,合法的情况下才能继续下去。
2.5.6 处理当前接收者
当前的接收者进程,判断有两种情况:
- 当前接收者进程存在,直接执行 processCurBroadcastLocked(r, app);这个函数最终也会回调到performReceive中。
- 当前接收者进程如果不存在,需要创建一个新的进程,然后调用scheduleBroadcastsLocked(...),这个函数就是如队列的函数,重新开始一遍。
3.performReceive回调函数
前面我们描述调用过程的时候,描述的重点都是performReceive(...)函数,现在要揭秘一下这个函数到底在执行什么?
InnerReceiver内部类
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
//......
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
try {
if (extras != null) {
extras.setAllowFds(false);
}
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
这儿又调用到ReceiverDispatcher.performReceive(...)
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);
//......
if (intent == null || !mActivityThread.post(args.getRunnable())) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManager.getService();
args.sendFinished(mgr);
}
}
}
}
这儿看到了一个执行这样一段话:
if (intent == null || !mActivityThread.post(args.getRunnable()))
这个判断条件,尤其是后一个mActivityThread.post(args.getRunnable()),这儿肯定执行了什么。
下面到执行的run()方法中总结代码,提取其执行要点:
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
这儿才是onReceive(...)的地方,但是我们知道,这里只是注册,还没有发送广播,但是可以确定的时,发送广播只是改变一些开关,保证执行到这里。
感谢大家,下文继续分享 发送广播。