我们先来看看源码中对goAsync
方法的注释
/**
* This can be called by an application in {@link #onReceive} to allow
* it to keep the broadcast active after returning from that function.
* This does <em>not</em> change the expectation of being relatively
* responsive to the broadcast (finishing it within 10s), but does allow
* the implementation to move work related to it over to another thread
* to avoid glitching the main UI thread due to disk IO.
* 可以在onReceive方法中调用,使得在onReceive方法返回之后广播仍处于活动状态。
* 这并不会改变广播广播超时时间,但是允许将其相关的工作移动到其他线程中处理,
* 避免由于磁盘IO导致的主线程故障。
*/
public final PendingResult goAsync();
简单来说,goAsync
提供了一种机制,让我们可以在异步线程中处理广播消息,以防止主线程被阻塞。
我们知道AMS
是通过跨进程调用ApplicationThread
来实现调用广播的onReceive
方法。
在正式开始介绍goAsync
之前,ApplicationThread
内部的实现逻辑。
ApplicationThread
中有两个方法处理广播:scheduleReceiver
和scheduleRegisteredReceiver
,对应静态广播接收者和动态广播接收者。
1 ApplicationThread是如何处理广播的
1.1 动态广播处理
scheduleRegisteredReceiver
方法内部仅是调用了IIntentReceiver.performReceive
方法。IIntentReceiver
是我们在调用register***
方法时方法内部创建的,它的实现类是InnerReceiver
是LoadedApk
的内部类。performReceive
方法会经历下面一系列的方法调用:
IIntentReceiver.performReceive
->
ReceiverDispatcher.performReceive
->
Handler.post
最终进入Args.run
方法。
public void run() {
//mReceiver就是我们注册的广播接收者
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
//log
final IActivityManager mgr = ActivityManagerNative.getDefault();
final Intent intent = mCurIntent;
mCurIntent = null;
//exception
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
setExtrasClassLoader(cl);
//关键点
//在调用onReceive之前会调用setPendingResult
//Args派生自PendingResult
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
} catch (Exception e) {
//exception
}
//onReceive方法结束之后
//如果getPendingResult不为null
//那么调用PendingResult.finish方法
if (receiver.getPendingResult() != null) {
finish();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
BroadcastReceiver
的setPendingResult
和getPendingResult
方法非常简单,就是BroadcastReceiver.mPendingResult
字段的get
、set
方法。
我们来看看PendingResult.finish
方法。
public final void finish() {
//mType有三个值TYPE_COMPONENT、TYPE_REGISTERED、TYPE_UNREGISTERED
//分别代表静态广播接收者、已注册的广播接收者和已注销的广播接收者
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManagerNative.getDefault();
if (QueuedWork.hasPendingWork()) {
//...
QueuedWork.singleThreadExecutor().execute( new Runnable() {
@Override public void run() {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast after work to component " + mToken);
sendFinished(mgr);
}
});
} else {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to component " + mToken);
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to " + mToken);
final IActivityManager mgr = ActivityManagerNative.getDefault();
sendFinished(mgr);
}
}
该方法最终调用了sendFinished
方法,值得注意的是只有静态广播接收者以及有序广播场景下的动态接收者才会走到sendFinished
方法中。通过之前的对于广播派发流程的分析,我们知道静态广播接收者都是有序的。因此可以说只有按顺序被接收的广播才会进入到sendFinished
中。
public void sendFinished(IActivityManager am) {
synchronized (this) {
//...
try {
//...
if (mOrderedHint) {
//有序广播
am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras, mAbortBroadcast, mFlags);
} else {
//静态广播接收者
am.finishReceiver(mToken, 0, null, null, false, mFlags);
}
} catch (RemoteException ex) {
}
}
}
sendFinished
的工作就是回调AMS.finishReceiver方法
。
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
//...
final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;
synchronized (this) {
BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
//查找对应的有序广播
r = queue.getMatchingOrderedReceiver(who);
if (r != null) {
//finishReceiverLocked会清理当前广播的一些状态
//然后决定是否可以处理下一个接收者
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
}
//doNext为true,则继续处理有序广播的下一个接收者
if (doNext) {
r.queue.processNextBroadcast(false);
}
trimApplications();
} finally {
Binder.restoreCallingIdentity(origId);
}
}
AMS.finishReceiver
其实是个承上启下的作用
- 上一个广播接收者已经完成接收工作,清理当前广播的一些数据
- 决定是否开始处理下一个广播接收者。
1.2 静态广播处理
静态广播接收者的处理方法为scheduleReceiver
,该方法会向主线程抛一个RECEIVER
消息,然后进入ActivityThread.handleReceiver
方法中
private void handleReceiver(ReceiverData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManagerNative.getDefault();
BroadcastReceiver receiver;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess();
data.setExtrasClassLoader(cl);
//通过反射构造一个接收者实例
receiver = (BroadcastReceiver) cl.loadClass(component).newInstance();
} catch (Exception e) {
//exception
}
try {
Application app = packageInfo.makeApplication(false, mInstrumentation);
//log
//下面的过程与动态广播接收者如出一辙
ContextImpl context = (ContextImpl) app.getBaseContext();
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
//exception
} finally {
sCurrentBroadcastIntent.set(null);
}
if (receiver.getPendingResult() != null) {
data.finish();
}
}
静态广播接收者的处理过程与处理动态广播接收者的过程如出一辙,唯一的不同点是对于静态广播接收者,系统还需要通过反射其创建对应的实例。
接下来的过程我们在分析动态广播接收者的时候都已经分析的差不多了,这里不再赘述。
1.3 总结
通过上面的分析,我们可以大致总结出,下面这个流程
而对于无序广播中的动态接收者,这个过程是没有意义的。因为通过对AMS.finishReceiver
方法的分析,我们可以知道这整个过程其实是有序(包括静态广播接收者)广播按顺序广播的一个机制。因此无序广播中的动态接收者源码中根本就不会进入到sendFinished
中。
2 goAsync方法
现在回过头来看看goAsync
方法
public final PendingResult goAsync() {
PendingResult res = mPendingResult;
mPendingResult = null;
return res;
}
这个方法非常简单,返回mPendingResult
并将其设置为null
。
如果我们在onReceive
方法中调用该方法,这意味着广播处理流程被打断了,当onReceive
方法执行完毕,由于mPendingResult
为null
,因此并不会马上回调AMS.finishReceiver
方法。而且由于goAsync
返回了PendingResult
,因此我们可以任意时刻、任意线程去调用PendingResult.finish
去回调AMS
。相当于将一个同步回调变成了异步回调。
而在这异步回调过程中,我们可以新起线程进行一些耗时的IO操作等等。
3 总结
结合1和2,我们不难得出一个结论:goAsync
其实是有序广播场景下进行异步广播处理的一个解决方案。
同时它的粒度非常细,可以精确到单个广播。因此goAsync
绝对是有序耗时广播场景下处理广播的一大利器。