[Framework] 记录 BroadcastReceiver 导致的 can't deliver broadcast 崩溃
在我们公司的多个应用上经常能够看到以下的崩溃:
android.app.RemoteServiceException: can't deliver broadcast
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1813)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
最后记录到的崩溃方法栈在 ActivityThread
中的 H
Handler
对象:
// ...
case SCHEDULE_CRASH:
throw new RemoteServiceException((String)msg.obj);
// ...
其实这个崩溃方法栈没有什么太大的作用,这个崩溃是由 system_server
进程通过 binder
发送过来的,就是简单通知应用进程出现了异常,导致这个崩溃还有更加深层次的原因。
我在 StackOverflow
中也看到了有人提出这个问题:
StackOverflow
总结下有两种解决方案:
- 在
onResuem()
和onPause()
中去注册/反注册BroadcastReceiver
导致有几率出现上面的崩溃,所以将BroadcastReceiver
的注册与反注册移动到onCreate()
和onDestory()
生命周期中能够解决这个问题。 - 使用
LocalBroadcastManager
来发送本地广播,不过假如你要是监听的是系统的广播,这个方法就没有办法使用。(在我自己看来进程内传递消息,我并不会使用BroadcastReceiver
)
我发现我们的应用崩溃都是在 API 30
及其以下的机型,而且都是在 Activity
进入 onPause()
生命周期后导致的崩溃,所以我就怀疑导致我们崩溃的就是解决方案1中的原因,在我写这篇文章的时候还没有验证这种修改方案是否有效。
在 Google Issue Tracker
中有人提出 Android 13
上有这个问题,不过我自己倒是没有在 Android 13
上遇到这个问题:
借助这个契机,我想简单看看 Broadcast
工作的原理,如果对源码不感兴趣的同学就不用看后面的内容了。
源码阅读基于 Android 11
注册广播
应用进程
我们注册广播通常是调用 Context#registerReceiver()
方法,最终会调用到 ContextImpl#registerReceiver()
方法:
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext(), 0);
}
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
// 这个对象非常重要,它是 AMS 通知广播来了的 binder 服务端
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
// scheduler 是回调广播的的线程,默认是主线程
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
// 通过 LoadedApk#getReceiverDispatcher() 方法来获取 IIntentReceiver 对象
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
// 通过 binder 调用 AMS#registerReceiverWithFeature() 方法
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
filter, broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
经过各种绕,会调用 registerReceiverInternal()
方法,这里会通过 LoadedApk#getReceiverDispatcher()
方法来生成一个 IIntentReceiver
对象,这个对象非常重要,它是一个 binder
的 Server
,用来接收 AMS
发送过来的广播消息。然后通过 ActivityManager#getService()
方法来获取 AMS
在客户端的 binder
的 Client
,然后通过 registerReceiverWithFeature()
方法来通知 AMS
。
我们再来看看 LoadedApk#getReceiverDispatcher()
方法的实现:
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> 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<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
上面的代码比较简单,应用中的所有的 BroadcastReceiver
都会被储存在 mReceivers
中,其中也包含了接收 AMS
广播的 binder
的 Server
对象,也就是 IIntentReceiver
,IIntentReceiver
的实现类是 ReceiverDispatcher
。
我们再看看 ReceiverDispatcher
的构造函数:
ReceiverDispatcher(BroadcastReceiver receiver, Context context,
Handler activityThread, Instrumentation instrumentation,
boolean registered) {
if (activityThread == null) {
throw new NullPointerException("Handler must not be null");
}
// 构建 binder 的 server 的实现
mIIntentReceiver = new InnerReceiver(this, !registered);
// 这个 receiver 对象就是我们传入的 BroadcastReceiver。
mReceiver = receiver;
mContext = context;
mActivityThread = activityThread;
mInstrumentation = instrumentation;
mRegistered = registered;
mLocation = new IntentReceiverLeaked(null);
mLocation.fillInStackTrace();
}
我们再看看 InnerReceiver
的构造函数的实现:
InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
朴实无华的构造函数,只是用弱引用来持有 ReceiverDispatcher
实例,这里先提前说明下,有广播来的时候会通过 binder
调用 InnerReceiver#performReceive()
方法。
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
if (intent == null) {
Log.wtf(TAG, "Null intent received");
rd = null;
} else {
rd = mDispatcher.get();
}
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction()
+ " seq=" + seq + " to " + (rd != null ? rd.mReceiver : null));
}
if (rd != null) {
// 调用 ReceiverDispatcher#performReceive() 方法
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to unregistered receiver");
IActivityManager mgr = ActivityManager.getService();
try {
if (extras != null) {
extras.setAllowFds(false);
}
// 通知 AMS 广播处理完成
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) {
// 将广播的各种参数用 Args 对象封装
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (intent == null) {
Log.wtf(TAG, "Null intent received");
} else {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
+ " seq=" + seq + " to " + mReceiver);
}
}
// 然后获取执行的线程(默认是主线程),来执行 Args#getRunnable() 方法生成的任务。
if (intent == null || !mActivityThread.post(args.getRunnable())) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManager.getService();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
args.sendFinished(mgr);
}
}
}
我们继续追踪 Args#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);
// 调用我们的 BroadcastReceiver#onReceiver() 方法
receiver.onReceive(mContext, intent);
} 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();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
};
上面其他的代码都不用太关心,主要是会调用我们传入的 BroadcastReceiver#onReceive()
回调方法,这后续的处理我们就非常熟悉了。
system_server 进程
在前面我们讲到了应用进程注册 BroadcastReceiver
时,会通过 binder
调用 AMS#registerReceiverWithFeature()
方法,我们来看看它的实现:
public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
String callerFeatureId, IIntentReceiver receiver, IntentFilter filter,
String permission, int userId, int flags) {
// ...
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
// 将 receiver (这个就是应用进程传递过来的 binder,主要用于将广播通知给应用进程) 封装到 ReceiverList
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
final int totalReceiversForApp = rl.app.receivers.size();
if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
throw new IllegalStateException("Too many receivers, total of "
+ totalReceiversForApp + ", registered for pid: "
+ rl.pid + ", callerPackage: " + callerPackage);
}
将 ReceiverList 添加到 App 中
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
// 将 ReceiverList 保存到 mRegisteredReceivers 成员变量中
mRegisteredReceivers.put(receiver.asBinder(), rl);
}
// ...
// 将 IntentFilter 和 ReceiverList 封装到 BroadcastFilter 对象中。
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
permission, callingUid, userId, instantApp, visibleToInstantApps);
if (rl.containsFilter(filter)) {
Slog.w(TAG, "Receiver with filter " + filter
+ " already registered for pid " + rl.pid
+ ", callerPackage is " + callerPackage);
} else {
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
// 将 BroadcastFilter 对象添加到 mReceiverResolver 成员变量中
mReceiverResolver.addFilter(bf);
}
// ...
}
我上面的代码中删掉了很多我们不关注的代码。简单来说上面的代码就是将 receiver
(这个就是应用进程传递过来的 binder
,主要用于将广播通知给应用进程) 封装到 ReceiverList
对象,并把它添加到 mRegisteredReceivers
成员变量中。将上面的 ReceiverList
对象和 IntentFilter
封装到 BroadcastFilter
对象,并把它添加到 mReceiverResolver
成员变量中。
发送广播
我们不考虑发送有序广播哈。
应用进程
我们发送广播是调用 Conrtext#sendBroadcast()
方法,它最终会到 ContextImpl#sendBroadcast()
方法中去。
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
开门见山,直接通过 binder
调用 AMS#broadcastIntentWithFeature()
方法。
system_server 进程
我们来看看 AMS#broadcastIntentWithFeature()
方法的实现:
public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
return broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, bOptions, serialized, sticky,
callingPid, callingUid, callingUid, callingPid, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
继续调用 broadcastIntentLocked()
方法:
@GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, 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,
@Nullable int[] broadcastWhitelist) {
// ...
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// ...
} else {
// 从 mReceiverResolver 去查询当前 intent 对应的 BroadcastFilter(其中包含订阅时的 Receiver)
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
// ...
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
// 获取 BroadcastQueue 实例
final BroadcastQueue queue = broadcastQueueForIntent(intent);
// 将重要的广播信息封装成 BroadcastRecord 对象,也包含重要的 registeredReceivers 列表
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, 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) {
// 将 BroadcastRecord 添加到并行广播队列中
queue.enqueueParallelBroadcastLocked(r);
// 触发 BroadcastQueue 任务
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
// ...
}
我删除掉了很多其他的代码,感兴趣的同学可以自己去找来看。从 mReceiverResolver
去查询当前 intent
对应的 BroadcastFilter
,(其中包含订阅时的 Receiver
,在上面讲订阅广播的时候讲到了)查询到的 BroadcastFilter
列表存放在本地变量 registeredReceivers
中。然后将很多的重要变量封装在 BroadcastRecord
对象中,也包括上面的 registeredReceivers
,然后将 BroadcastRecord
通过 BroadcastQueue#enqueueParallelBroadcastLocked()
方法添加到并行广播队列中,然后通过 BroadcastQueue#scheduleBroadcastsLocked()
方法来触发 BroadcastQueue
中的任务。
我们来看看 BroadcastQueue#enqueueParallelBroadcastLocked()
方法的实现:
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
mParallelBroadcasts.add(r);
enqueueBroadcastHelper(r);
}
朴实无华的代码,直接将 BroadcastRecord
添加到 mParallelBroadcasts
成员变量列表中。
继续看看 scheduleBroadcastsLocked()
是如何触发任务的:
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;
}
直接通过 Handler
来触发,对应的是 BROADCAST_INTENT_MSG
的 Message
。
我们再来看看 Handler
是怎么处理这个消息的:
@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;
}
}
收到 BROADCAST_INTENT_MSG
消息后,会调用 processNextBroadcast()
方法。
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
processNextBroadcastLocked(fromMsg, false);
}
}
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
// ...
mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
}
// First, deliver any non-serialized broadcasts right away.
// 遍历并行的广播列表
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
// ...
}
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
// 遍历 BroadcastRecoder 中对应的 BroadcastFilter
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
// ...
// 将广播通知给 BroadcastFilter 中的 Receiver
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
// ...
}
// ...
}
遍历并行的 BroadcastRecord
列表,然后遍历 BroadcastRecoder
中的 BroadcastFilter
列表(其中就包含我们订阅广播时的 receiver
),然后通过 deliverToRegisteredReceiverLocked()
方法来下发广播至 Receiver
。
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
// ...
r.receiverTime = SystemClock.uptimeMillis();
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
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);
}
// ...
}
然后继续调用 performReceiveLocked()
方法。
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
if (app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
// 通过 binder 通知客户端,最后会触发对应的 Broadcast#onReceive() 方法回调
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
// DeadObjectException when the process isn't actually dead.
//} catch (DeadObjectException ex) {
// Failed to call into the process. It's dying so just let it die and move on.
// throw ex;
} catch (RemoteException ex) {
// 通过 binder 通知应用进程出现错误
// Failed to call into the process. It's either dying or wedged. Kill it gently.
synchronized (mService) {
Slog.w(TAG, "Can't deliver broadcast to " + app.processName
+ " (pid " + app.pid + "). Crashing it.");
// 将广播错误的信息,通知给应用进程
app.scheduleCrash("can't deliver broadcast");
}
throw ex;
}
} else {
// Application has died. Receiver doesn't exist.
throw new RemoteException("app.thread must not be null");
}
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
在上面的方法已经能够看到我们的崩溃信息了,首先通过 thread#scheduleRegisteredReceiver()
方法向应用进程发送广播消息,如果工作正常我们注册的 BroadcastReceiver#onReceive()
方法就会触发。如果这个过程出现了异常,就会向应用进程再发送 can't deliver broadcast
异常,也就是我们最开始看到的崩溃信息。
在 binder
的通信过程中有两种大的情况会触发 RemoteException
:
- 远端
binder
Server
本身的实现方法报错。 - 通信本身中断导致报错。
我们看到源码中的注释描述这种情况是:
Failed to call into the process. It's either dying or wedged. Kill it gently.
也就是当前进程正在结束或者被卡住了。
最后
本篇文章简单介绍了广播接收器 can't deliver broadcast
异常的简单处理方式,然后还简单看了一下 Broadcast
的工作方式,希望这些内容能够对你有帮助。