当前位置:首页 > Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析(1)
//broadcastPermission与静态注册中的permission标签对应,用于对广播发送方的权限进行限制
//只有拥有对应权限的发送方,发送的广播才能被此receiver接收
//不指定scheduler时,receiver收到广播后,将在主线程调用onReceive函数 //指定scheduler后,onReceive函数由scheduler对应线程处理
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {
//ContextImpl是Context家族中实际工作的对象,getOuterContext得到的是ContextImpl对外的代理
//一般为Application、Activity、Service等
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext()); }
与上述代码类似,不论调用Context中的哪个接口注册BroadcastReceiver,最终流程均会进入到registerReceiverInternal函数。
现在,我们跟进一下registerReceiverInternal函数:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context) { IIntentReceiver rd = null;
if (receiver != null) {
//mPackageInfo的类型为LoadedApk
if (mPackageInfo != null && context != null) { if (scheduler == null) {
//未设置scheduler时,将使用主线程的handler处理
//这就是默认情况下,Receiver的onReceive函数在主线程被调用的原因 scheduler = mMainThread.getHandler(); }
//getReceiverDispatcher函数的内部,利用BroadcastReceiver构造出ReceiverDispatcher
//返回ReceiverDispatcher中的IIntentReceiver对象
//IIntentReceiver是注册到AMS中,供AMS回调的Binder通信接口 rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler,
mMainThread.getInstrumentation(), true); } else {
//这段代码写的我瞬间懵逼了。。。scheduler == null对应的判断,明显可以移出
if-else结构的
//不符合大google的逼格 if (scheduler == null) {
scheduler = mMainThread.getHandler(); }
//主动创建
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver(); } }
try {
//将Receiver注册到AMS
final Intent intent = ActivityManagerNative.getDefault().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId); .................
} catch (RemoteException e) { ........... } }
以上代码中主要的工作是:
创建BroadcastReceiver对应的ReceiverDispatcher,得到ReceiverDispatcher内部的IIntentReceiver对象,
然后利用Binder通信将该对象连同BroadcastReceiver的其它信息注册到AMS中。
IIntentReceiver对应的继承关系如下图所示:
结合上图,我们需要知道的是:
1、BroadcastReceiver收到的广播实际上是AMS发送的,因此BroadcastReceiver与AMS之间必须进行Binder通信。
从类图可以看出,BroadcastReceiver及其内部成员并没有直接继承Binder类。
负责与AMS通信的,实际上是定义于LoadedApk中的InnerReceiver类,该类继承IIntentReceiver.Stub,作为Binder通信的服务端。
从上文的代码可以看出,注册BroadcastReceiver时,会将该对象注册到AMS中供其回调。
当InnerReceiver收到AMS的通知后,将会调用ReceiverDispatcher进行处理。
由于ReceiverDispatcher持有了实际的BroadcastReceiver,于是最终将广播递交给BroadcastReceiver的onReceive函数进行处理。
以上的设计思路实际上是:
将业务接口(处理广播的BroadcastReceiver类)与通信接口(InnerReceiver类)分离,由统一的管理类(ReceiverDispatcher)进行衔接。
这基本是Java层使用Binder通信的标配,AIDL本身也是这种思路。
2、BroadcastRecevier中定义了一个PendingResult类,该类用于异步处理广播消息。
从上文的代码我们知道了,如果没有明确指定BroadcastReceiver对应的handler,那么其onReceive函数将在主线程中被调用。
因此当onReceive中需要执行较耗时的操作时,会阻塞主线程,影响用户体验。 为了解决这种问题,可以在onReceive函数中采用异步的方式处理广播消息。
一提到异步的方式处理广播消息,大家可能会想到在BroadcastReceiver的onReceive中单独创建线程来处理广播消息。 然而,这样做存在一些问题。
例如:
对于一个静态注册的BroadcastReceiver 对象,对应的进程初始时可能并没有启动。 当AMS向这个BroadcastReceiver对象发送广播时,才会先启动对应的进程。
一旦BroadcastReceiver对象处理完广播,i并将返回结果通知给AMS后,AMS就有可能清理掉对应进程。
因此,若在onReceive中创建线程处理问题,那么onReceive函数就可能在线程完成工作前返回,导致AMS提前销毁进程。
此时,线程也会消亡,使得工作并没有有效完成。
为了解决这个问题,就可以用到BroadcastReceiver提供的PendingResult。 具体的做法是:
先调用BroadcastReceiver的goAsync函数得到一个PendingResult对象, 然后将该对象放到工作线程中去释放。
这样onReceive函数就可以立即返回而不至于阻塞主线程。
同时,Android系统将保证BroadcastReceiver对应进程的生命周期, 直到工作线程处理完广播消息后,调用PendingResult的finish函数为止。
其中原理是:
正常情况下,BroadcastReceiver在onReceive函数结束后,
判断PendingResult不为null,才会将处理完毕的信息通知给AMS。
一旦调用BroadcastReceiver的goAsync函数,就会将BroadcastReceiver中的PendingResult置为null,
因此即使onReceive函数返回,也不会将信息通知给AMS。 AMS也就不会处理BroadcastReceiver对应的进程。
待工作线程调用PendingResult的finish函数时,才会将处理完毕的信息通知给AMS。
代码示例如下:
private class MyBroadcastReceiver extends BroadcastReceiver { ..................
public void onReceive(final Context context, final Intent intent) { //得到PendingResult
final PendingResult result = goAsync();
//放到异步线程中执行
AsyncHandler.post(new Runnable() { @Override
public void run() {
handleIntent(context, intent);//可进行一些耗时操作 result.finish(); } }); } }
final class AsyncHandler {
private static final HandlerThread sHandlerThread = new HandlerThread(\ private static final Handler sHandler;
static {
sHandlerThread.start();
sHandler = new Handler(sHandlerThread.getLooper()); }
public static void post(Runnable r) { sHandler.post(r); }
private AsyncHandler() {} }
共分享92篇相关文档