前言
前端时间,在项目中有个问题:有应用反馈收到开机广播的时间过长,距离开机过去可能半分钟了,才收到开机广播,严重影响交互。
带着这个问题,我只得查看发送广播的源码实现,并排查以下可能原因:
- 开机广播是否是有序广播?
- 安卓系统是怎样处理并发送有序广播、无序广播的?
- 是什么原因,导致开机广播的传递花费这么长时间?
这里先说出部分结果:
-
开机广播是有序广播
在广播接收器的中,可以直接通过
isOrderedBroadcast
验证 -
广播先并行处理无序广播,再串行处理有序广播
最后一个问题,从源码分析去找答案把!
源码
基于Android 10
广播由ActivityManager管理,所以主要代码都在frameworks/base/services/core/java/com/android/server/am/
目录下
开机广播发送前准备
UserController
当用户系统加载完成,会通知ActiviyManagerService
执行unlockUser
,并在一大串方法调用链下,走到UserController.java
的finishUserUnlockedCompleted
方法,进行开机广播发送前的准备工作。
java
//UserController.java
void finishUserUnlocked(final UserState uss) {
Slog.d(TAG, "UserController event: finishUserUnlocked(" + userId + ")");
...
if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
...
} else {
finishUserUnlockedCompleted(uss);
}
}
private void finishUserUnlockedCompleted(UserState uss) {
...
Slog.i(TAG, "Posting BOOT_COMPLETED user #" + userId);
...
//开机广播intent定义
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_OFFLOAD);
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
FgThread.getHandler().post(() -> {
mInjector.broadcastIntent(bootIntent, null,new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
//1.当所有接收器处理完开机广播,会回调该方法
Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u"
+ userId);
mBootCompleted = true;
}
}, 0, null, null,new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},AppOpsManager.OP_NONE, null,
true,//2.这个参数就可以表明,开机广播是有序广播
false, MY_PID, SYSTEM_UID,callingUid, callingPid, userId);
});
}
static class Injector {
private final ActivityManagerService mService;
protected int broadcastIntent(Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered/*3.代表广播是否有序*/,
boolean sticky, int callingPid, int callingUid, int realCallingUid,
int realCallingPid, int userId) {
synchronized (mService) {
//4.交给ActivityManagerService去发广播
return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid,
userId);
}
}
}
从图中的2、3
注释可以看出,开机广播是有序广播,所以第一个疑问解决
AMS
从上面代码可以看到,UserController
最终调用AMS的broadcastIntentLocked
,该方法是重载方法,为广播发送前做最后准备
java
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
int realCallingPid, int userId) {
return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
false /* allowBackgroundActivityStarts */);
}
@GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
// 1.一系列的检查工作,会抛出异常
// 2.获取注册了该广播的广播接收器
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// 3.静态注册的广播
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
// 4. 动态注册的广播
if (intent.getComponent() == null) {
....
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, users[i]);
....
}
// 5.先发送无序广播给动态注册的接收器
if (!ordered && NR > 0) {
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending
&& (queue.replaceParallelBroadcastLocked(r) != null);
// Note: We assume resultTo is null for non-ordered broadcasts.
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
// 6.Merge into one list.
int ir = 0;
if (receivers != null) {
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
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;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
....
// 7.
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, timeoutExempt);
....
} else {
queue.enqueueOrderedBroadcastLocked(r);//存放无序广播的静态接收器、有序广播的动态\静态接收器
queue.scheduleBroadcastsLocked();
}
}
这个方法的代码非常长,但主要就是干了这几件事:
- 事先的一系列检查:包括权限、当前系统所处的状态,如果不符合发送广播条件,就抛出异常
- 获取注册了该广播的静态接收器和动态接收器
- 如果广播是无序广播,则先发送给动态注册的接收器,因为动态注册的接收器,其应用已经起来
- 按照广播接收器的优先级,合并动态接收器和静态接收器(注意这里可能包含三种类型的广播接收器:无序广播的静态注册接收器、有序广播的静态\动态接收器)
发送广播
可以看到,无论是有序广播还是无序广播,AMS最终把广播接收器作为参数传给了BroadcastRecord
(后面简化为BR)对象,然后再调用BroadcastQueue
的两个方法:
java
queue.enqueueParallelBroadcastLocked(r);//无序广播调用
queue.enqueueOrderedBroadcastLocked(r);//有序广播调用
queue.scheduleBroadcastsLocked();
第一个方法enqueueOrderedBroadcastLocked
:
java
//BroadcastQueue.java
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
mDispatcher.enqueueOrderedBroadcastLocked(r);
enqueueBroadcastHelper(r);
}
java
//BroadcastDispatcher.java
private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
...
void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
mOrderedBroadcasts.add(r);
}
可以看到BR被放到了BroadcastDispatcher
的列表存放了起来。
第二个方法scheduleBroadcastsLocked
:
java
//BroadcastQueue.java
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
...
final BroadcastHandler mHandler;
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;
}
}
}
//BroadcastQueue.java
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
processNextBroadcastLocked(fromMsg, false);
}
}
scheduleBroadcastsLocked
里面,利用handler发送消息,handler处理事件,调用processNextBroadcast
分段来分析:
第一段
java
//发送广播的主要代码
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
}
// 1.First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
final int N = r.receivers.size();
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
}
如果mParallelBroadcasts
不为空,发送所有无序广播
第二段
java
if (mPendingBroadcast != null) {
boolean isDead;
if (mPendingBroadcast.curApp.pid > 0) {
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(
mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.isCrashing();
}
} else {
final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.pendingStart;
}
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
mPendingBroadcast
存放的是即将发送的广播,并且等待广播接收器的应用起来,所以这一段是判断这个广播的应用起来没。
第三段:
java
boolean looped = false;
do {
final long now = SystemClock.uptimeMillis();
//1
r = mDispatcher.getNextBroadcastLocked(now);
if (r == null) {
...
return;
}
boolean forceReceive = false;
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (!r.deferred) {
final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
if (mDispatcher.isDeferringLocked(receiverUid)) {
BroadcastRecord defer;
if (r.nextReceiver + 1 == numReceivers) {
} else {
// 2.
defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
}
mDispatcher.addDeferredBroadcast(receiverUid, defer);
r = null;
looped = true;
continue;
}
}
} while (r == null);
这是一个循环,我主要是看如何满足条件跳出循环。
注释一,取出一个BR,并且保存到mCurrentBroadcast,直到处理完成前,都会一直是这个BR
java
//BroadcastDispatcher.java
public BroadcastRecord getNextBroadcastLocked(final long now) {
if (mCurrentBroadcast != null) {
return mCurrentBroadcast;
}
final boolean someQueued = !mOrderedBroadcasts.isEmpty();
BroadcastRecord next = null;
if (next == null && someQueued) {
next = mOrderedBroadcasts.remove(0);
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG, "Next broadcast from main queue: " + next);
}
}
mCurrentBroadcast = next;
return next;
}
注释二,找到该userID下所有注册该广播的接收器,并且deferred置为true
经过第一次循环这两个注释的处理,下一次取出的r!=null,并且defferred为true,就可以跳出循环。
第三段
java
// 应用已经起来
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;
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
// 应用还没起来
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");
....
return;
}
maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
第三段代码只贴了最后几句,前面的大部分都在判断该广播是否该skip。
最后几句代码:
- 当前要发送的广播接收器的应用已经起来,直接发送
- 当前要发送的广播接收器的应用还没起来,继续等待。
- 将当前代码保存到
mPendingBroadcast
等待处理完毕。
这里其实就已经得到答案:
-
对于有序广播,通过静态注册的广播接收器,要等待应用起来,然后才处理广播并调用
onReceive
方法。 -
如果某个应用的启动过程比较长,那会阻塞广播的传递,增加后面应用的等待时间。