上一篇文章我们主要梳理了广播的动态注册,发送,接收,主要的数据结构以及需要大家严重注意的几点内容,相信大家已经熟悉了。没看过的朋友传送门:BroadcastReceiver详细分析之主要流程
相信聪明的朋友已经发现,上篇基本没怎么介绍串行广播和静态注册器,基本介绍的是普通广播的动态注册器的流程。因此,本篇单独讲解串行广播以及静态注册器的相关调度以及超时机制,毕竟这块知识点还是很多的,值得单开一篇文章。
本篇代码仍然基于原生Android Q
一.有序广播的基本使用
BroadcastReceiver的注册、发送和接收的主要流程和核心数据结构都已经讲解完。本篇主要讲述有序广播的调度和超时,无序广播都是并行发送的,不存在优先级和回调机制,因此也不会存在超时机制。这个好理解吧?还是再提一下吧,无序广播的动态注册器接收在上篇中都是循环直接发送的,属于binder的one way调用,AMS不需要等待它们执行结果。而有序广播需要根据接收器的优先级一个个发送,优先级高的接收器可以拦截广播等等,既然有先来后到,那么咱就只能一个个发了,并且前面的接收器要是处理时间太长怎么办?这不是影响后面的接收器了嘛,因此肯定会有超时机制。OK,废话不多说,我们开始:
下面是有序广播的基本使用方法:
//定义广播
BroadcastReceiver receiver=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(MainActivity.this,"这是xiaomiapp2",Toast.LENGTH_LONG).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter intentFilter=new IntentFilter("a.b.c");
registerReceiver(receiver,intentFilter); //注册广播
......
Intent intent=new Intent(); //发送广播
intent.setAction("a.b.c");
sendOrderedBroadcast(intent,null);
平平无奇的代码,不需要过多说。
二.串行广播的调度过程
根据之前介绍,串行广播是将动态注册和静态注册的接收器根据优先级合并在一起,然后统一调度。无序广播都是one way binder call,因此发完就发完了,不存在通知AMS,而有序广播发完是需要通知AMS的,因此需要一个一个的来,让我们回到BroadcastQueue的processNextBroadcastLocked方法:
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
//......无序动态广播的处理,不再赘述
// Now take care of the next serialized one...
boolean looped = false;
do {
final long now = SystemClock.uptimeMillis();
//取出有序广播一个BroadcastRecord任务
r = mDispatcher.getNextBroadcastLocked(now);
if (r == null) { //没有更多任务了
// No more broadcasts are deliverable right now, so all done!
mDispatcher.scheduleDeferralCheckLocked(false);
mService.scheduleAppGcsLocked();
if (looped) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
}
// when we have no more ordered broadcast on this queue, stop logging
if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
mLogLatencyMetrics = false;
}
return;
}
boolean forceReceive = false;
// Ensure that even if something goes awry with the timeout
// detection, we catch "hung" broadcasts here, discard them,
// and continue to make progress.
//
// This is only done if the system is ready so that early-stage receivers
// don't get executed with timeouts; and of course other timeout-
// exempt broadcasts are ignored.
//取出广播任务包含的所有接收器数量
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
//......
// Get the next receiver...
int recIdx = r.nextReceiver++; //取出该任务中的下一个接收器
//......
final BroadcastOptions brOptions = r.options;
final Object nextReceiver = r.receivers.get(recIdx);
//有序广播中的动态注册的广播,动态注册的广播是BroadcastFilter实例,静态注册的是ResolveInfo实例
if (nextReceiver instanceof BroadcastFilter) {
// Simple case: this is a registered receiver who gets
// a direct call.
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
//同样调用deliverToRegisteredReceiverLocked方法
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
+ mQueueName + "]: ordered="
+ r.ordered + " receiver=" + r.receiver);
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
} else {
if (filter.receiverList != null) {
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
// r is guaranteed ordered at this point, so we know finishReceiverLocked()
// will get a callback and handle the activity start token lifecycle.
}
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
}
}
return;
}
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
boolean skip = false;
//......关于广播是否跳过的一系列判断
r.manifestCount++;
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ receiverUid);
}
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(receiverUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
}
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.curComponent.getPackageName(), false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.curComponent.getPackageName() + ": " + e);
}
// Is this receiver's application already running?
//判断接收器进程是否存在,存在才会执行,这里适用于静态注册的广播
if (app != null && app.thread != null && !app.killed) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
maybeAddAllowBackgroundActivityStartsToken(app, r);
processCurBroadcastLocked(r, app, skipOomAdj); //处理静态注册的有序广播
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when sending broadcast to "
+ r.curComponent, e);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Failed sending broadcast to "
+ r.curComponent + " with " + r.intent, e);
// If some unexpected exception happened, just skip
// this broadcast. At this point we are not in the call
// from a client, so throwing an exception out from here
// will crash the entire system instead of just whoever
// sent the broadcast.
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
r.state = BroadcastRecord.IDLE;
return;
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
// Not running -- get it started, to be executed when the app comes up.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Need to start app ["
+ mQueueName + "] " + targetProcess + " for broadcast " + r);
if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord("broadcast", r.curComponent),
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
+ info.activityInfo.applicationInfo.packageName + "/"
+ receiverUid + " for broadcast "
+ r.intent + ": process is bad");
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
2.1动态注册接收器的循环
上面动态注册的那部分进入了deliverToRegisteredReceiverLocked方法,我们看看:
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
//......
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
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 {
r.receiverTime = SystemClock.uptimeMillis();
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
//进入performReceiveLocked
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
// parallel broadcasts are fire-and-forget, not bookended by a call to
// finishReceiverLocked(), so we manage their activity-start token here
if (r.allowBackgroundActivityStarts && !r.ordered) {
postActivityStartTokenRemoval(filter.receiverList.app, r);
}
}
//......
后面类似于上篇无序广播的方法调用链,最终进入args.getRunnable方法:
public final Runnable getRunnable() {
return () -> {
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
if (ActivityThread.DEBUG_BROADCAST) {
int seq = mCurIntent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction()
+ " seq=" + seq + " to " + mReceiver);
Slog.i(ActivityThread.TAG, " mRegistered=" + mRegistered
+ " mOrderedHint=" + ordered);
}
final IActivityManager mgr = ActivityManager.getService();
final Intent intent = mCurIntent;
if (intent == null) {
Log.wtf(TAG, "Null intent being dispatched, mDispatched=" + mDispatched
+ (mRunCalled ? ", run() has already been called" : ""));
}
mCurIntent = null;
mDispatched = true;
mRunCalled = true;
if (receiver == null || intent == null || mForgotten) {
if (mRegistered && ordered) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing null broadcast to " + mReceiver);
sendFinished(mgr);
}
return;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent); //回调onReceiver方法
} catch (Exception e) {
if (mRegistered && ordered) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing failed broadcast to " + mReceiver);
sendFinished(mgr);
}
if (mInstrumentation == null ||
!mInstrumentation.onException(mReceiver, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Error receiving broadcast " + intent
+ " in " + mReceiver, e);
}
}
if (receiver.getPendingResult() != null) {
finish(); //调用Pendingresult的finish方法
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
};
}
}
上述的finish方法,无序广播也会走到,具体的分叉就在finish方法里面啦:
public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManager.getService();
if (QueuedWork.hasPendingWork()) {
// If this is a broadcast component, we need to make sure any
// queued work is complete before telling AM we are done, so
// we don't have our process killed before that. We now know
// there is pending work; put another piece of work at the end
// of the list to finish the broadcast, so we don't block this
// thread (which may be the main thread) to have it finished.
//
// Note that we don't need to use QueuedWork.addFinisher() with the
// runnable, since we know the AM is waiting for us until the
// executor gets to it.
QueuedWork.queue(new Runnable() {
@Override public void run() {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast after work to component " + mToken);
sendFinished(mgr);
}
}, false);
} 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 = ActivityManager.getService();
sendFinished(mgr); //这里会进入sendFinished方法
}
}
上述代码段的mOrderedHint决定了无序广播不会走到里面的sendFinish方法,这就是所谓的分叉口啦,我们继续:
public void sendFinished(IActivityManager am) {
synchronized (this) {
if (mFinished) {
throw new IllegalStateException("Broadcast already finished");
}
mFinished = true;
try {
if (mResultExtras != null) {
mResultExtras.setAllowFds(false);
}
if (mOrderedHint) { //有序广播值为true
//通知AMS广播接收完成
am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
mAbortBroadcast, mFlags);
} else {
// This broadcast was sent to a component; it is not ordered,
// but we still need to tell the activity manager we are done.
am.finishReceiver(mToken, 0, null, null, false, mFlags);
}
} catch (RemoteException ex) {
}
}
}
接着又是一次binder call,咱们来到AMS:
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who);
// Refuse possible leaked file descriptors
if (resultExtras != null && resultExtras.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Bundle");
}
final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;
BroadcastQueue queue;
synchronized(this) {
if (isOnOffloadQueue(flags)) {
queue = mOffloadBroadcastQueue;
} else {
queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
}
//根据Ibinder对象,这里也就是IIntentReceiver查出BroadcastRecord
r = queue.getMatchingOrderedReceiver(who);
if (r != null) { //判断是否进行下一次分发
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
if (doNext) { //如果需要继续分发,则继续调用processNextBroadcastLocked
r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
}
// updateOomAdjLocked() will be done here
trimApplicationsLocked(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
后面又进入到熟悉的processNextBroadcastLocked方法了,会执行下一个receiver的发送,不再赘述咯
2.2 静态注册接收器的循环
根据第二节的代码,静态接收器会执行到processCurBroadcastLocked方法:
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app, boolean skipOomAdj) throws RemoteException {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " for app " + app);
if (app.thread == null) {
throw new RemoteException();
}
if (app.inFullBackup) {
skipReceiverLocked(r);
return;
}
r.receiver = app.thread.asBinder();
r.curApp = app;
app.curReceivers.add(r);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.mProcessList.updateLruProcessLocked(app, false, null);
if (!skipOomAdj) {
mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
// Tell the application to launch this receiver.
r.intent.setComponent(r.curComponent);
boolean started = false;
try {
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Delivering to component " + r.curComponent
+ ": " + r);
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
//同样调用IApplicationThread接口进入APP进程
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.getReportedProcState());
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
} finally {
if (!started) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + ": NOT STARTED!");
r.receiver = null;
r.curApp = null;
app.curReceivers.remove(r);
}
}
}
同样调用IApplicationThread接口进入APP进程,
查看ApplicationThread的scheduleReceiver方法:
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
updateProcessState(processState, false);
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
sendMessage(H.RECEIVER, r); //进入sendMessage方法
}
进入sendMessage方法:
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) {
Slog.v(TAG,
"SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);
}
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg); //熟悉的主线程Handler
}
进入主线程的handleMessage方法:
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
case RECEIVER:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
handleReceiver((ReceiverData)msg.obj); //what=RECEIVER,执行
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
进入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 = ActivityManager.getService();
Application app;
BroadcastReceiver receiver;
ContextImpl context;
try {
app = packageInfo.makeApplication(false, mInstrumentation);
context = (ContextImpl) app.getBaseContext();
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
java.lang.ClassLoader cl = context.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess();
data.setExtrasClassLoader(cl);
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {
if (DEBUG_BROADCAST) Slog.i(TAG,
"Finishing failed broadcast to " + data.intent.getComponent());
data.sendFinished(mgr);
throw new RuntimeException(
"Unable to instantiate receiver " + component
+ ": " + e.toString(), e);
}
try {
if (localLOGV) Slog.v(
TAG, "Performing receive of " + data.intent
+ ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + packageInfo.getPackageName()
+ ", comp=" + data.intent.getComponent().toShortString()
+ ", dir=" + packageInfo.getAppDir());
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data); //设置pendingResult
receiver.onReceive(context.getReceiverRestrictedContext(), //回调onReceiver方法
data.intent);
} catch (Exception e) {
if (DEBUG_BROADCAST) Slog.i(TAG,
"Finishing failed broadcast to " + data.intent.getComponent());
data.sendFinished(mgr);
if (!mInstrumentation.onException(receiver, e)) {
throw new RuntimeException(
"Unable to start receiver " + component
+ ": " + e.toString(), e);
}
} finally {
sCurrentBroadcastIntent.set(null);
}
if (receiver.getPendingResult() != null) {
data.finish(); //ReceiverData继承于PendingResult,因此后面仍然是调用PendingResult的finish方法
}
}
到这里就和动态注册接收器的finish方法一样啦,不再赘述咯
2.3 静态注册接收器进程的启动
上述几个过程都是APP启动的情况下进程广播接收的,那么在APP未启动的情况下,是怎样的接收过程呢?答案显而易见,AMS会启动APP进程,不过国内手机厂商在这一块把控比较严格,拿小米手机来说,应用安装后默认是关闭自启动权限的,需要打开自启动权限,这样才能启动APP进程。让我们回到BroadcastQueue的processNextBroadcastLocked
方法:
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
//......
// Is this receiver's application already running?
//这里是APP进程已启动的情况下,上面已经介绍过
if (app != null && app.thread != null && !app.killed) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
maybeAddAllowBackgroundActivityStartsToken(app, r);
processCurBroadcastLocked(r, app, skipOomAdj);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when sending broadcast to "
+ r.curComponent, e);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Failed sending broadcast to "
+ r.curComponent + " with " + r.intent, e);
// If some unexpected exception happened, just skip
// this broadcast. At this point we are not in the call
// from a client, so throwing an exception out from here
// will crash the entire system instead of just whoever
// sent the broadcast.
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
r.state = BroadcastRecord.IDLE;
return;
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
//进程未启动,需要启动它
// Not running -- get it started, to be executed when the app comes up.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Need to start app ["
+ mQueueName + "] " + targetProcess + " for broadcast " + r);
//通过AMS的startProcessLocked启动
if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord("broadcast", r.curComponent),
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) { //为null的话表示进程启动失败
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
+ info.activityInfo.applicationInfo.packageName + "/"
+ receiverUid + " for broadcast "
+ r.intent + ": process is bad");
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE; //重置状态,发给下一个接收器,直接return
return;
}
maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
mPendingBroadcast = r; //不为null则把广播任务r赋给mPendingBroadcast
mPendingBroadcastRecvIndex = recIdx;
}
看到这里,大家可能觉得APP进程启动成功后也没看到处理广播的操作啊?是的,这个方法到这里就没了,聪明的你肯定想到了,肯定是在进程启动后的某个地方执行了广播,是的,you are so clever。
我们知道,Zygote进程孵化出APP进程后,会执行ActivityThread.main()方法,好的,我们去main方法里面看看:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper(); //创建主线程looper
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq); //调用thread.attach方法
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
继续,我们去attach方法里面去看看:
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
//这边binder调用了AMS的attachApplication方法
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
//......
}
继续到AMS里面去看看:
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
//调用attachApplicationLocked方法
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
继续走着:
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
//......
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) { //检查是否有待执行的广播
try {
//执行sendPendingBroadcastsLocked
didSomething |= sendPendingBroadcastsLocked(app);
checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
//......
}
继续走着:
boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
//这里实际上遍历了所有的广播队列,如前台广播,后台广播等等
for (BroadcastQueue queue : mBroadcastQueues) {
//调用sendPendingBroadcastsLocked
didSomething |= queue.sendPendingBroadcastsLocked(app);
}
return didSomething;
}
继续:
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast; //之前的mPendingBroadcast就在这里派上用场啦
if (br != null && br.curApp.pid > 0 && br.curApp.pid == app.pid) {
if (br.curApp != app) {
Slog.e(TAG, "App mismatch when sending pending broadcast to "
+ app.processName + ", intended target is " + br.curApp.processName);
return false;
}
try {
mPendingBroadcast = null;
processCurBroadcastLocked(br, app, false); //熟悉的函数
didSomething = true;
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting receiver "
+ br.curComponent.flattenToShortString(), e);
logBroadcastReceiverDiscardLocked(br);
finishReceiverLocked(br, br.resultCode, br.resultData,
br.resultExtras, br.resultAbort, false);
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
br.state = BroadcastRecord.IDLE;
throw new RuntimeException(e.getMessage());
}
}
return didSomething;
}
上述代码中,之前的mPendingBroadcast就在这里派上用场啦,
后面就是调用processCurBroadcastLocked,跟上面的流程一样啦,不再赘述咯
三.串行广播的超时机制
既然有序广播需要通知AMS我已经接收完毕,那么肯定存在一个计时器来判断某个接收器是否超时,那么超时机制在哪设置的呢?聪明的你肯定又想到了,肯定是在分发有序广播的时候嘛,是的,you are very clever
我们回到BroadcastQueue的分发函数processNextBroadcastLocked里面看看:
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
//......
if (! mPendingBroadcastTimeoutMessage) { //这里会判断BroadcastHandler里面是否存在超时消息
//这里会定义超时消息的时间,receiverTime是分发给当前接收器的时间,mConstants.TIMEOUT是超时时间,前台广播10s,后台广播60s
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Submitting BROADCAST_TIMEOUT_MSG ["
+ mQueueName + "] for " + r + " at " + timeoutTime);
setBroadcastTimeoutLocked(timeoutTime); //没有的话咱们就设置一个吧
}
final BroadcastOptions brOptions = r.options;
final Object nextReceiver = r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter) {
//......
}
Ok,我们继续查看setBroadcastTimeoutLocked:
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) { //二次校验是否有超时消息
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime); //在超时时间到了之后发送消息
mPendingBroadcastTimeoutMessage = true; //变量置为true
}
}
继续,BroadcastHandler又见面了:
private final class BroadcastHandler extends Handler {
public BroadcastHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
+ mQueueName + "]");
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true); //这次是执行超时方法
}
} break;
}
}
}
继续查看:
final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) { //如果是来自handler的消息
mPendingBroadcastTimeoutMessage = false; //变量置为false,表示又可以再发超时消息了
}
if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
return;
}
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
if (!mService.mProcessesReady) {
// Only process broadcast timeouts if the system is ready; some early
// broadcasts do heavy work setting up system facilities
return;
}
// If the broadcast is generally exempt from timeout tracking, we're done
if (r.timeoutExempt) {
if (DEBUG_BROADCAST) {
Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+ r.intent.getAction());
}
return;
}
//重点看看这边,这个now肯定是执行到这里的时间,也就是原先设置的超时时间点
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
//这里的r.receiverTime是目前执行的receiver的分发时间点,如果满足,表示上一个接收器没有超时
if (timeoutTime > now) {
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes. Instead, we set up
// an initial timeout then kick it down the road a little further as needed
// when it expires.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Premature timeout ["
+ mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ timeoutTime);
//既然没有超时,那么就重新设置超时时间点
setBroadcastTimeoutLocked(timeoutTime);
return; //此时对于超时消息的处理就返回了,也就是说本次函数调用不存在超时
}
}
//......
// Move on to the next receiver. //如果超时了,就执行下一个接收器
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
if (!debugging && anrMessage != null) { //打印ANR信息
// Post the ANR to the handler since we do not want to process ANRs while
// potentially holding our lock.
mHandler.post(new AppNotResponding(app, anrMessage));
}
}
我这里写了一个demo,xiaomiapp2在onReceive()里面执行耗时操作,发生了ANR,而xiaomiapp在超时之后收到了广播,这是超时之后的log:
log很清楚,xiaomiapp2发生了ANR,但是不影响下一个接收器,xiaomiapp继续收到了广播并弹出Toast。
当串行广播接收器全部处理完毕后,取消超时消息:
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) { //广播分发完毕
//......
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
cancelBroadcastTimeoutLocked(); //取消超时消息
//......
final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this); //移除超时消息
mPendingBroadcastTimeoutMessage = false;
}
}
不得不说,Android在超时机制这一块还是设计的很巧妙简洁的。
四.总结
串行广播的调度和超时机制也讲完咯,大家看到这里也挺累的,此外,还有一种广播叫粘性广播(sticky),不过Android官方已经不推荐使用了,我也就不说了。大家主要还是关注串行广播的整个循环调度的过程,以及启动进程和超时机制,毕竟平常在排查广播的相关问题时还是需要了解原理的。
有什么不对之处欢迎指出,大家一起学习,一起进步!