BroadcastReceiver导致的ANR源码分析

505 阅读4分钟

我们知道在BroadcastReceiver的onReceive方法中执行耗时操作可能导致ANR, 跟踪源码看看anr是如何触发的。

有序广播的使用

// 1.定义广播接收器
public class PriorityBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "PriorityBroadcast";
    @Override
    public void onReceive(Context context, Intent intent) {
        String resultData = getResultData();
        Log.d(TAG, "优先级为1的接收到的广播数据resultData---->" + resultData);
    }
}

// 2.1 在AndroidManifest.xml中注册广播接收器
<receiver android:name=".broadcast.PriorityBroadcastReceiver"
    android:exported="true">
    <intent-filter android:priority="1">
        <action android:name="order.test.example.xxx" />
    </intent-filter>
</receiver>

// 2.2 或在代码中动态注册广播

val intentFilter = IntentFilter()
intentFilter.addAction(“order.test.example.xxx”)
registerReceiver(receiver, intentFilter)

// 3.发送广播
val intent = Intent()
intent.putExtra("data", "有序广播通过intent.putExtra传递的数据")
intent.action = "order.test.example.xxx"
val bundle = Bundle()
bundle.putString("order_data", "有序广播通过bundle传递的数据")
sendOrderedBroadcast(intent, null, PriorityBroadcastReceiver2(), handler, Activity.RESULT_OK,"MainActivity发送了一个有序广播", bundle)

广播的动态注册流程

下面看看广播的动态注册流程:

Activity.registerReceiver(receiver, intentFilter) -> 
ContextImpl.registerReceiver(xxx) ->
ContextImpl.registerReceiverInternal(xxx) ->
ActivityManagerService.registerReceiverWithFeature(xxx) ->

来看看在ActivityManagerService中注册广播的方法

public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,String callerFeatureId, String receiverId, IIntentReceiver receiver,IntentFilter filter, String permission, int userId, int flags) {
    ...
    // 添加到mRegisteredReceivers中
    ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
    if (rl == null) {
        // 新建一个ReceiverList保存Receiver和对应的filter
        rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                userId, receiver);
        if (rl.app != null) {
            final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
            if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                throw new IllegalStateException("Too many receivers, total of "
                        + totalReceiversForApp + ", registered for pid: "
                        + rl.pid + ", callerPackage: " + callerPackage);
            }
            rl.app.mReceivers.addReceiver(rl);
        } else {
            try {
                receiver.asBinder().linkToDeath(rl, 0);
            } catch (RemoteException e) {
                return sticky;
            }
            rl.linkedToDeath = true;
        }
        mRegisteredReceivers.put(receiver.asBinder(), rl);
    }
    ...
    // 创建filter对象
    BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                    receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
                    exported);
    if (rl.containsFilter(filter)) {
        Slog.w(TAG, "Receiver with filter " + filter
                + " already registered for pid " + rl.pid
                + ", callerPackage is " + callerPackage);
    } else {
        // 把filter添加到ReceiverList中保存
        rl.add(bf);
        if (!bf.debugCheck()) {
            Slog.w(TAG, "==> For Dynamic broadcast");
        }
        // 保存filter
        mReceiverResolver.addFilter(getPackageManagerInternal().snapshot(), bf);
    }
    ...
}

就是把Receiver和IntentFilter保存起来,等发送广播的时候就可以查找。

发送广播的流程

Activity.sendBroadcast(intent) ->
ContextImpl.sendBroadcast(xxx) ->
ActivityManagerService.broadcastIntentWithFeature() ->
ActivityManagerService.broadcastIntentLocked() ->

来看看broadcastIntentLocked方法的实现:

final int broadcastIntentLocked() {
...
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
    // 从packageManager查找匹配的receiver 
    receivers = collectReceiverComponents(
            intent, resolvedType, callingUid, users, broadcastAllowList);
}
if (intent.getComponent() == null) {
    final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
    if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
        // Query one target user at a time, excluding shell-restricted users
        for (int i = 0; i < users.length; i++) {
            if (mUserController.hasUserRestriction(
                    UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                continue;
            }
            // 查找
            List<BroadcastFilter> registeredReceiversForUser =
                    mReceiverResolver.queryIntent(snapshot, intent,
                            resolvedType, false /*defaultOnly*/, users[i]);
            if (registeredReceivers == null) {
                registeredReceivers = registeredReceiversForUser;
            } else if (registeredReceiversForUser != null) {
               registeredReceivers.addAll(registeredReceiversForUser);
            }
        }
    } else {
        // 查找
        registeredReceivers = mReceiverResolver.queryIntent(snapshot, 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);
    }
    final BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
            callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
            requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
            registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered,
            sticky, false, userId, allowBackgroundActivityStarts,
            backgroundActivityStartsToken, 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;
}
...
// 有序广播
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
        callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
        requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
        receivers, resultTo, resultCode, resultData, resultExtras,
        ordered, sticky, false, userId, allowBackgroundActivityStarts,
        backgroundActivityStartsToken, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
final BroadcastRecord oldRecord =
        replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
if (oldRecord != null) {
    // Replaced, fire the result-to receiver.
    if (oldRecord.resultTo != null) {
        final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
        try {
           oldRecord.mIsReceiverAppRunning = true;
           oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
                    oldRecord.intent,
                    Activity.RESULT_CANCELED, null, null,
                    false, false, oldRecord.userId, oldRecord.callingUid, callingUid,
                    SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
        } catch (RemoteException e) {
            Slog.w(TAG, "Failure ["
                    + queue.mQueueName + "] sending broadcast result of "
                    + intent, e);
        }
    }
} else {
    // 加入有序广播队列
    queue.enqueueOrderedBroadcastLocked(r);
    //  广播分发
    queue.scheduleBroadcastsLocked();
}
 ...
}

查找符合条件的广播接收者,然后按照有序广播和无序广播分别加到对应的队列中,然后调用BroadcastQueue的scheduleBroadcastsLocked方法开始分发广播到广播接收者,处理的方法是processNextBroadcastLocked

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
... 
// 处理无序广播
 while (mParallelBroadcasts.size() > 0) {
    r = mParallelBroadcasts.remove(0);
    r.dispatchTime = SystemClock.uptimeMillis();
    r.dispatchRealTime = SystemClock.elapsedRealtime();
    r.dispatchClockTime = System.currentTimeMillis();
    r.mIsReceiverAppRunning = true;
    ...
    final int N = r.receivers.size();
    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["

            + mQueueName + "] " + r);
    for (int i=0; i<N; i++) {
        Object target = r.receivers.get(i);
        if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                "Delivering non-ordered on [" + mQueueName + "] to registered "
                + target + ": " + r);
        deliverToRegisteredReceiverLocked(r,
                (BroadcastFilter) target, false, i);
    }
    addBroadcastToHistoryLocked(r);
    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
            + mQueueName + "] " + r);
    }
}
... 
// 处理有序广播 取下一个
int recIdx = r.nextReceiver++;
// 记录接收时间
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
    r.dispatchTime = r.receiverTime;
    r.dispatchRealTime = SystemClock.elapsedRealtime();
    r.dispatchClockTime = System.currentTimeMillis();
    ...
}
if (! mPendingBroadcastTimeoutMessage) {
    long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
    // 设置超时时间
    setBroadcastTimeoutLocked(timeoutTime);
}

final BroadcastOptions brOptions = r.options;
final Object nextReceiver = r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter) {
    BroadcastFilter filter = (BroadcastFilter)nextReceiver;
    r.mIsReceiverAppRunning = true;
    // 发送广播
    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.
        }
    }
    return;
}

无序广播直接一次性调了所有的广播接收器;有序广播时逐个调用的,所以只有有序广播才设置了超时时间。我们先如何调到应用进程的onReceive的。

BroadcastQueue.deliverToRegisteredReceiverLocked() -> 
BroadcastQueue.performReceiveLocked() ->
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser,
            int receiverUid, int callingUid, long dispatchDelay,
            long receiveDelay) throws RemoteException {
    // Send the intent to the receiver asynchronously using one-way binder calls.

if (app != null) {
    final IApplicationThread thread = app.getThread();
    if (thread != null) {
        // If we have an app thread, do the call through that so it is
        // correctly ordered with other one-way calls.
        try {
            // 调到ApplicationThread
            thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                    data, extras, ordered, sticky, sendingUser,
                    app.mState.getReportedProcState());
        } catch (RemoteException ex) {
            // Failed to call into the process. It's either dying or wedged. Kill it gently.
            synchronized (mService) {
                final String msg = "Failed to schedule " + intent + " to " + receiver
                        + " via " + app + ": " + ex;
                Slog.w(TAG, msg);
                app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
                        ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
            }
            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);
}
...
}

接着调到了ApplicationThread:

ApplicationThread.scheduleRegisteredReceiver() ->
// frameworks/base/core/java/android/app/LoadedApk.java
InnerReceiver.performReceive() ->
ReceiveDispatcher.performReceive() -> 
mActivityThread.post(args.getRunnable()) -> 

看看runnable的代码

try {
    ClassLoader cl = mReceiver.getClass().getClassLoader();
    intent.setExtrasClassLoader(cl);
    // TODO: determine at registration time if caller is
    // protecting themselves with signature permission
    intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
            mContext.getAttributionSource());
    setExtrasClassLoader(cl);
    receiver.setPendingResult(this);
    // 调用onReceive方法
    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();
}

反射创建了BroadcastReceiver对象,并调用了Receiver的onReceive方法,最后调用了finish() 方法告诉ActivityManagerService广播结束。这样ActivityManagerService就可以执行下一个广播了。在执行下一个广播前会把前一个广播的超时检测给取消。

cancelBroadcastTimeoutLocked();

再来看看如果没有及时取消,会怎么样:

final void broadcastTimeoutLocked(boolean fromMsg) {
        ...
        ProcessRecord app = null;
        String anrMessage = null;
        Object curReceiver;
        if (r.nextReceiver > 0) {
            curReceiver = r.receivers.get(r.nextReceiver-1);
            r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
        } else {
            curReceiver = r.curReceiver;
        }
        ...
        if (app != null) {
            anrMessage = "Broadcast of " + r.intent.toString();
        }
        if (mPendingBroadcast == r) {
            mPendingBroadcast = null;
        }

        finishReceiverLocked(r, r.resultCode, r.resultData,
                r.resultExtras, r.resultAbort, false);
        scheduleBroadcastsLocked();
        
        if (!debugging && anrMessage != null) {
            // 开始anr处理流程
            mService.mAnrHelper.appNotResponding(app, anrMessage);
        }
    }

这里就直接开始anr的处理流程,这个部分的处理和service是一致的,具体流程可以参考上一篇

总结:

  1. 有序广播才会触发anr;
  2. 前一个广播处理完了,在处理下一个时就会把前一个的超时处理任务给取消掉;如果没有及时取消就会走到anr的处理流程;
  3. anr的处理流程和service是相同的。