[Framework] 记录 BroadcastReceiver 导致的 can't deliver broadcast 崩溃

[Framework] 记录 BroadcastReceiver 导致的 can't deliver broadcast 崩溃

在我们公司的多个应用上经常能够看到以下的崩溃:

text 复制代码
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 对象:

Java 复制代码
// ...
case SCHEDULE_CRASH:  
    throw new RemoteServiceException((String)msg.obj);
// ...    

其实这个崩溃方法栈没有什么太大的作用,这个崩溃是由 system_server 进程通过 binder 发送过来的,就是简单通知应用进程出现了异常,导致这个崩溃还有更加深层次的原因。

我在 StackOverflow 中也看到了有人提出这个问题:
StackOverflow

总结下有两种解决方案:

  1. onResuem()onPause() 中去注册/反注册 BroadcastReceiver 导致有几率出现上面的崩溃,所以将 BroadcastReceiver 的注册与反注册移动到 onCreate()onDestory() 生命周期中能够解决这个问题。
  2. 使用 LocalBroadcastManager 来发送本地广播,不过假如你要是监听的是系统的广播,这个方法就没有办法使用。(在我自己看来进程内传递消息,我并不会使用 BroadcastReceiver

我发现我们的应用崩溃都是在 API 30 及其以下的机型,而且都是在 Activity 进入 onPause() 生命周期后导致的崩溃,所以我就怀疑导致我们崩溃的就是解决方案1中的原因,在我写这篇文章的时候还没有验证这种修改方案是否有效。

Google Issue Tracker 中有人提出 Android 13 上有这个问题,不过我自己倒是没有在 Android 13 上遇到这个问题:

Google Issue Tracker

借助这个契机,我想简单看看 Broadcast 工作的原理,如果对源码不感兴趣的同学就不用看后面的内容了。

源码阅读基于 Android 11

注册广播

应用进程

我们注册广播通常是调用 Context#registerReceiver() 方法,最终会调用到 ContextImpl#registerReceiver() 方法:

Java 复制代码
    @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 对象,这个对象非常重要,它是一个 binderServer,用来接收 AMS 发送过来的广播消息。然后通过 ActivityManager#getService() 方法来获取 AMS 在客户端的 binderClient,然后通过 registerReceiverWithFeature() 方法来通知 AMS

我们再来看看 LoadedApk#getReceiverDispatcher() 方法的实现:

Java 复制代码
    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 广播的 binderServer 对象,也就是 IIntentReceiverIIntentReceiver 的实现类是 ReceiverDispatcher

我们再看看 ReceiverDispatcher 的构造函数:

Java 复制代码
        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 的构造函数的实现:

Java 复制代码
            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }

朴实无华的构造函数,只是用弱引用来持有 ReceiverDispatcher 实例,这里先提前说明下,有广播来的时候会通过 binder 调用 InnerReceiver#performReceive() 方法。

Java 复制代码
            @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() 方法。

Java 复制代码
        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() 方法是如何构建任务的:

Java 复制代码
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() 方法,我们来看看它的实现:

Java 复制代码
    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() 方法中去。

Java 复制代码
    @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() 方法的实现:

Java 复制代码
    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() 方法:

Java 复制代码
    @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() 方法的实现:

Java 复制代码
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
    mParallelBroadcasts.add(r);
    enqueueBroadcastHelper(r);
}

朴实无华的代码,直接将 BroadcastRecord 添加到 mParallelBroadcasts 成员变量列表中。

继续看看 scheduleBroadcastsLocked() 是如何触发任务的:

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;
    }

直接通过 Handler 来触发,对应的是 BROADCAST_INTENT_MSGMessage

我们再来看看 Handler 是怎么处理这个消息的:

Java 复制代码
        @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() 方法。

Java 复制代码
    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

Java 复制代码
    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() 方法。

Java 复制代码
    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

  1. 远端 binder Server 本身的实现方法报错。
  2. 通信本身中断导致报错。

我们看到源码中的注释描述这种情况是:

Failed to call into the process. It's either dying or wedged. Kill it gently.

也就是当前进程正在结束或者被卡住了。

最后

本篇文章简单介绍了广播接收器 can't deliver broadcast 异常的简单处理方式,然后还简单看了一下 Broadcast 的工作方式,希望这些内容能够对你有帮助。

相关推荐
代码or搬砖1 小时前
String字符串
android·java·开发语言
5980354153 小时前
【java工具类】小数、整数转中文大写
android·java·开发语言
TAEHENGV4 小时前
进度跟踪模块 Cordova 与 OpenHarmony 混合开发实战
android·javascript·数据库
酸菜牛肉汤面4 小时前
6、索引算法有哪些?
android
青春勿语4 小时前
Lumen:重新定义 Android 图片加载体验
android·glide
TAEHENGV5 小时前
回收站模块 Cordova 与 OpenHarmony 混合开发实战
android·java·harmonyos
TAEHENGV6 小时前
创建目标模块 Cordova 与 OpenHarmony 混合开发实战
android·java·开发语言
zjw_swun6 小时前
Compose原理简易实现
android·composer
青莲8437 小时前
Kotlin Flow 深度探索与实践指南——中部:实战与应用篇
android·前端
建群新人小猿8 小时前
陀螺匠企业助手-我的日程
android·大数据·运维·开发语言·容器