Android四大组件系列8 Broadcast广播机制(下)

概述

广播 (Broadcast) 机制用于进程或线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者 BroadcastReceiver 是 Android 四大组件之一。BroadcastReceiver 分为两类:

  • 静态广播接收者:通过 AndroidManifest.xml 的标签来声明的 BroadcastReceiver
  • 动态广播接收者:通过 AMS.registerReceiver() 方式注册的 BroadcastReceiver,动态注册更为灵活,可在不需要时动态取消注册

PS:动态 BroadcastReceiver 比较简单,静态的就麻烦一些了,因为在广播发送之时,静态 receiver 所从属的进程可能还没有启动呢,这就需要先启动新的进程,费时费力。另一方面,有些时候用户希望广播能够按照一定顺序发送,为此,Android 又搞出了 ordered broadcast 的概念。

从广播发送方式可分为三类:

  • 普通广播:通过 Context.sendBroadcast() 发送,并行处理
  • 有序广播:通过 Context.sendOrderedBroadcast() 发送,串行处理
  • Sticky 广播:通过 Context.sendStickyBroadcast() 发送

以上是对广播机制的概述,我们接上篇 Android四大组件系列7 Broadcast广播机制(上)继续分析

4.4 BroadcastQueue.processNextBroadcast

从 processNextBroadcast() 的代码,我们就可以看清楚前面说的 “平行广播”、“有序广播” 和 “动态 receiver”、“静态 receiver” 之间的关系了。

可以说:processNextBroadcast 函数是广播处理的核心。

我们在前文已经说过,所有的静态 receiver 都是串行处理的,而动态 receiver 则会按照发广播时指定的方式,进行 “并行” 或 “串行” 处理。

能够并行处理的广播,其对应的若干 receiver 一定都已经存在了,不会牵扯到启动新进程的操作,所以可以在一个 while 循环中,一次性全部 deliver。

而有序广播,则需要一个一个地处理,其循环处理的手段是发送事件,也就是说,在一个 receiver 处理完毕后,会利用广播队列(BroadcastQueue)的 mHandler,发送一个 BROADCAST_INTENT_MSG 事件,从而执行下一次的 processNextBroadcast()。

BroadcastQueue.java

final void processNextBroadcast(boolean fromMsg) {
        synchronized (mService) {
            processNextBroadcastLocked(fromMsg, false);
        }
}

继续调用 processNextBroadcastLocked,这个函数很长,我们先看下全部的代码,然后下面进行详细分析。

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
        BroadcastRecord r;

        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
                + mQueueName + "]: "
                + mParallelBroadcasts.size() + " parallel broadcasts; "
                + mDispatcher.describeStateLocked());

        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();

            ......

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

        // Now take care of the next serialized one...

        // If we are waiting for a process to come up to handle the next
        // broadcast, then do nothing at this point.  Just in case, we
        // check that the process we're waiting for still exists.
        if (mPendingBroadcast != null) {
            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                    "processNextBroadcast [" + mQueueName + "]: waiting for "
                    + mPendingBroadcast.curApp);

            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 {
                Slog.w(TAG, "pending app  ["
                        + mQueueName + "]" + mPendingBroadcast.curApp
                        + " died before responding to broadcast");
                mPendingBroadcast.state = BroadcastRecord.IDLE;
                mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
                mPendingBroadcast = null;
            }
        }

        boolean looped = false;

        do {
            final long now = SystemClock.uptimeMillis();
            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;
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && !r.timeoutExempt && 
             r.dispatchTime > 0) {
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + 
                        (2 * mConstants.TIMEOUT * numReceivers))) {
                    Slog.w(TAG, "Hung broadcast ["
                            + mQueueName + "] discarded after timeout failure:"
                            + " now=" + now
                            + " dispatchTime=" + r.dispatchTime
                            + " startTime=" + r.receiverTime
                            + " intent=" + r.intent
                            + " numReceivers=" + numReceivers
                            + " nextReceiver=" + r.nextReceiver
                            + " state=" + r.state);
                    broadcastTimeoutLocked(false); // forcibly finish this broadcast
                    forceReceive = true;
                    r.state = BroadcastRecord.IDLE;
                }
            }

            if (r.state != BroadcastRecord.IDLE) {
                if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
                        "processNextBroadcast("
                        + mQueueName + ") called when not idle (state="
                        + r.state + ")");
                return;
            }

            // Is the current broadcast is done for any reason?
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                // Send the final result if requested
                if (r.resultTo != null) {
                    boolean sendResult = true;
                    if (r.splitToken != 0) {
                        int newCount = mSplitRefcounts.get(r.splitToken) - 1;
                        if (newCount == 0) {
                            mSplitRefcounts.delete(r.splitToken);
                        } else {
                            sendResult = false;
                            mSplitRefcounts.put(r.splitToken, newCount);
                        }
                    }
                    if (sendResult) {
                        try {
                            performReceiveLocked(r.callerApp, r.resultTo,
                                    new Intent(r.intent), r.resultCode,
                                    r.resultData, r.resultExtras, false,
                                    false, r.userId);
                            r.resultTo = null;
                        } catch (RemoteException e) {
                            r.resultTo = null;
                            Slog.w(TAG, "Failure ["
                                    + mQueueName + "] sending broadcast result of "
                                    + r.intent, e);
                        }
                    }
                }
                cancelBroadcastTimeoutLocked();

                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                        "Finished with ordered broadcast " + r);

                // ... and on to the next...
                addBroadcastToHistoryLocked(r);
                if (r.intent.getComponent() == null && r.intent.getPackage() == null
                        && (r.intent.getFlags()&
                        Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
                    mService.addBroadcastStatLocked(r.intent.getAction(),
                    r.callerPackage, r.manifestCount, r.manifestSkipCount,
                    r.finishTime-r.dispatchTime);
                }
                mDispatcher.retireBroadcastLocked(r);
                r = null;
                looped = true;
                continue;
            }

            if (!r.deferred) {
                final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
                if (mDispatcher.isDeferringLocked(receiverUid)) {
                    BroadcastRecord defer;
                    if (r.nextReceiver + 1 == numReceivers) {
                        defer = r;
                        mDispatcher.retireBroadcastLocked(r);
                    } else {
                        defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
                        if (DEBUG_BROADCAST_DEFERRAL) {
                            Slog.i(TAG_BROADCAST, "Post split:");
                            Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
                            for (int i = 0; i < r.receivers.size(); i++) {
                                Slog.i(TAG_BROADCAST, "  " + r.receivers.get(i));
                            }
                            Slog.i(TAG_BROADCAST, "Split receivers:");
                            for (int i = 0; i < defer.receivers.size(); i++) {
                                Slog.i(TAG_BROADCAST, "  " + defer.receivers.get(i));
                            }
                        }
                        // Track completion refcount as well if relevant
                        if (r.resultTo != null) {
                            int token = r.splitToken;
                            if (token == 0) {
                                r.splitToken = defer.splitToken = nextSplitTokenLocked();
                                mSplitRefcounts.put(r.splitToken, 2);
                            } else {
                                final int curCount = mSplitRefcounts.get(token);
                                mSplitRefcounts.put(token, curCount + 1);
                            }
                        }
                    }
                    mDispatcher.addDeferredBroadcast(receiverUid, defer);
                    r = null;
                    looped = true;
                    continue;
                }
            }
        } while (r == null);

        // Get the next receiver...
        int recIdx = r.nextReceiver++;

        // Keep track of when this receiver started, and make sure there
        // is a timeout message pending to kill it if need be.
        r.receiverTime = SystemClock.uptimeMillis();
        if (recIdx == 0) {
            r.dispatchTime = r.receiverTime;
            r.dispatchClockTime = System.currentTimeMillis();

            if (mLogLatencyMetrics) {
                StatsLog.write(
                        StatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
                        r.dispatchClockTime - r.enqueueClockTime);
            }

            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
                    System.identityHashCode(r));
                Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
                    System.identityHashCode(r));
            }
            
        }
        if (! mPendingBroadcastTimeoutMessage) {
            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) {
            // 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(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);
                }
                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;
        if (brOptions != null &&
                (info.activityInfo.applicationInfo.targetSdkVersion
                        < brOptions.getMinManifestReceiverApiLevel() ||
                info.activityInfo.applicationInfo.targetSdkVersion
                        > brOptions.getMaxManifestReceiverApiLevel())) {
            skip = true;
        }
        if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage,
            r.callingUid, component.getPackageName(),
             info.activityInfo.applicationInfo.uid)) {
            ......
            skip = true;
        }
        if (!skip) {
            skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
                    r.callingPid, r.resolvedType, 
                    info.activityInfo.applicationInfo.uid);
            if (skip) {
                ......
            }
        }
        int perm = mService.checkComponentPermission(info.activityInfo.permission,
                r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
                info.activityInfo.exported);
        if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
            if (!info.activityInfo.exported) {
                ......
            } else {
                ......
            }
            skip = true;
        } else if (!skip && info.activityInfo.permission != null) {
            final int opCode = AppOpsManager.permissionToOpCode(
                info.activityInfo.permission);
            if (opCode != AppOpsManager.OP_NONE
                    && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
                            r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
                ......
                skip = true;
            }
        }
        if (!skip && info.activityInfo.applicationInfo.uid != 
            Process.SYSTEM_UID && r.requiredPermissions != null &&
            r.requiredPermissions.length > 0) {
            for (int i = 0; i < r.requiredPermissions.length; i++) {
                String requiredPermission = r.requiredPermissions[i];
                try {
                    perm = AppGlobals.getPackageManager().
                            checkPermission(requiredPermission,
                              info.activityInfo.applicationInfo.packageName,
                              UserHandle
                              .getUserId(info.activityInfo.applicationInfo.uid));
                } catch (RemoteException e) {
                    perm = PackageManager.PERMISSION_DENIED;
                }
                if (perm != PackageManager.PERMISSION_GRANTED) {
                    Slog.w(TAG, "Permission Denial: receiving "
                            + r.intent + " to "
                            + component.flattenToShortString()
                            + " requires " + requiredPermission
                            + " due to sender " + r.callerPackage
                            + " (uid " + r.callingUid + ")");
                    skip = true;
                    break;
                }
                int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
                if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                        && mService.mAppOpsService.noteOperation(appOp,
                        info.activityInfo.applicationInfo.uid, 
                        info.activityInfo.packageName)
                        != AppOpsManager.MODE_ALLOWED) {
                    Slog.w(TAG, "Appop Denial: receiving "
                            + r.intent + " to "
                            + component.flattenToShortString()
                            + " requires appop " + AppOpsManager.permissionToOp(
                            requiredPermission)
                            + " due to sender " + r.callerPackage
                            + " (uid " + r.callingUid + ")");
                    skip = true;
                    break;
                }
            }
        }
        if (!skip && r.appOp != AppOpsManager.OP_NONE
                && mService.mAppOpsService.noteOperation(r.appOp,
                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
                != AppOpsManager.MODE_ALLOWED) {
            Slog.w(TAG, "Appop Denial: receiving "
                    + r.intent + " to "
                    + component.flattenToShortString()
                    + " requires appop " + AppOpsManager.opToName(r.appOp)
                    + " due to sender " + r.callerPackage
                    + " (uid " + r.callingUid + ")");
            skip = true;
        }
        boolean isSingleton = false;
        try {
            isSingleton = mService.isSingleton(info.activityInfo.processName,
                    info.activityInfo.applicationInfo,
                    info.activityInfo.name, info.activityInfo.flags);
        } catch (SecurityException e) {
            Slog.w(TAG, e.getMessage());
            skip = true;
        }
        if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
            if (ActivityManager.checkUidPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS,
                    info.activityInfo.applicationInfo.uid)
                            != PackageManager.PERMISSION_GRANTED) {                
                skip = true;
            }
        }
        if (!skip && info.activityInfo.applicationInfo.isInstantApp()
                && r.callingUid != info.activityInfo.applicationInfo.uid) {
            Slog.w(TAG, "Instant App Denial: receiving "
                    + r.intent
                    + " to " + component.flattenToShortString()
                    + " due to sender " + r.callerPackage
                    + " (uid " + r.callingUid + ")"
                    + " Instant Apps do not support manifest receivers");
            skip = true;
        }
        if (!skip && r.callerInstantApp
                && (info.activityInfo.flags & 
                    ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0
                && r.callingUid != info.activityInfo.applicationInfo.uid) {
            Slog.w(TAG, "Instant App Denial: receiving "
                    + r.intent
                    + " to " + component.flattenToShortString()
                    + " requires receiver have visibleToInstantApps set"
                    + " due to sender " + r.callerPackage
                    + " (uid " + r.callingUid + ")");
            skip = true;
        }
        if (r.curApp != null && r.curApp.isCrashing()) {
            // If the target process is crashing, just skip it.
            Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
                    + " to " + r.curApp + ": process crashing");
            skip = true;
        }
        if (!skip) {
            boolean isAvailable = false;
            try {
                isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
                        info.activityInfo.packageName,
                        UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
            } catch (Exception e) {
                // all such failures mean we skip this receiver
                Slog.w(TAG, "Exception getting recipient info for "
                        + info.activityInfo.packageName, e);
            }
            if (!isAvailable) {
                
                skip = true;
            }
        }   
        if (!skip) {
            if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
                    info.activityInfo.packageName, UserHandle.getUserId(
                            info.activityInfo.applicationInfo.uid))) {
                skip = true;
            }
        }

        // This is safe to do even if we are skipping the broadcast, and we need
        // this information now to evaluate whether it is going to be allowed to run.
        final int receiverUid = info.activityInfo.applicationInfo.uid;
        // If it's a singleton, it needs to be the same app or a special app
        if (r.callingUid != Process.SYSTEM_UID && isSingleton
                && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
            info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
        }
        String targetProcess = info.activityInfo.processName;
        ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                info.activityInfo.applicationInfo.uid, false);

        if (!skip) {
            final int allowed = mService.getAppStartModeLocked(
                    info.activityInfo.applicationInfo.uid, 
                    info.activityInfo.packageName,
                    info.activityInfo.applicationInfo.targetSdkVersion,
                     -1, true, false, false);
            if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                // We won't allow this receiver to be launched if the app has been
                // completely disabled from launches, or it was not explicitly sent
                // to it and the app is in a state that should not receive it
                // (depending on how getAppStartModeLocked has determined that).
                if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                    Slog.w(TAG, "Background execution disabled: receiving "
                            + r.intent + " to "
                            + component.flattenToShortString());
                    skip = true;
                } else if (((r.intent.getFlags()&
                    Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
                        || (r.intent.getComponent() == null
                            && r.intent.getPackage() == null
                            && ((r.intent.getFlags()
                                    & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
                            && !isSignaturePerm(r.requiredPermissions))) {
                    mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
                            component.getPackageName());
                    Slog.w(TAG, "Background execution not allowed: receiving "
                            + r.intent + " to "
                            + component.flattenToShortString());
                    skip = true;
                }
            }
        }

        if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
                && !mService.mUserController
                .isUserRunning(UserHandle.getUserId(
                info.activityInfo.applicationInfo.uid), 0 /* flags */)) {
            skip = true;
            
        }

        if (skip) {
            if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                    "Skipping delivery of ordered [" + mQueueName + "] "
                    + r + " for reason described above");
            r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
            r.receiver = null;
            r.curFilter = null;
            r.state = BroadcastRecord.IDLE;
            r.manifestSkipCount++;
            scheduleBroadcastsLocked();
            return;
        }
        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;
    }

processNextBroadcastLocked() 的代码逻辑大体是这样的:先尝试处理 BroadcastQueue 中的 “平行广播” 部分。这需要遍历并行列表(mParallelBroadcasts)的每一个 BroadcastRecord 以及其中的 receivers 列表。对于平行广播而言,receivers 列表中的每个子节点是个 BroadcastFilter。我们直接将广播递送出去即可:

// First, deliver any non-serialized broadcasts right away.
// 遍历 mParallelBroadcasts 取出其中的每一个 BroadcastRecord
while (mParallelBroadcasts.size() > 0) {
   r = mParallelBroadcasts.remove(0);
   r.dispatchTime = SystemClock.uptimeMillis();
   r.dispatchClockTime = System.currentTimeMillis();

   ......
   // 取出广播 BroadcastRecord 中对应的所有 receivers,然后发送广播
   final int N = r.receivers.size();
   
   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);
}

4.4.1 deliverToRegisteredReceiverLocked()

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {
        boolean skip = false;
        if (!mService.validateAssociationAllowedLocked(r.callerPackage,
            r.callingUid, filter.packageName, filter.owningUid)) {
            skip = true;
        }
        if (!skip && !mService.mIntentFirewall.checkBroadcast(r.intent, 
            r.callingUid, r.callingPid, r.resolvedType,
            filter.receiverList.uid)) {
            skip = true;
        }
        ......
        // 一堆判断条件,决定是否 skip,如果跳过了,则不会发送广播
        if (skip) {
            r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
            return;
        }
        ......
       // 到这里就开始执行广播的发送了
        r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
        
        if (ordered) {// 此时发送的是并行广播,这个为 false,不会走此分支
            ......
        }
        try {
            if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                    "Delivering to " + filter + " : " + r);
            if (filter.receiverList.app != null && 
                filter.receiverList.app.inFullBackup) {
               ......
            } else {
                r.receiverTime = SystemClock.uptimeMillis();
                maybeAddAllowBackgroundActivityStartsToken(
                    filter.receiverList.app, r);
                // 重点关注 filter.receiverList.receiver 参数,这个就是
                // IIntentReceiver,用来跨进程通信的
                performReceiveLocked(filter.receiverList.app,
                    filter.receiverList.receiver,
                    new Intent(r.intent), r.resultCode, r.resultData,
                    r.resultExtras, r.ordered, r.initialSticky, r.userId);
                if (r.allowBackgroundActivityStarts && !r.ordered) {
                    postActivityStartTokenRemoval(filter.receiverList.app, r);
                }
            }
            ......
        } catch (RemoteException e) {
            ......
        }
}

调用 performReceiveLocked 正式完成广播的发送。需要注意的是要想理解广播是怎么发送的,必须了解 BroadcastFilter 的数据结构组成,这个里边包含了 receiverList 这个变量,它里边的 app 是代表注册 BroadcastReceiver 应用的 ProcessRecord,而 receiverList 里边还有个变量 receiver 是最重要的,代表的是应用中注册的 BroadcastReceiver 对应的 ReceiverDispatcher 中的 IIntentReceiver 类型的 mIIntentReceiver,用来实现进程间的数据传输,这个可以参考 Android四大组件系列7 Broadcast广播机制(上) 的第二节动态 BroadcastReceiver 注册,里面有详细的介绍。

4.4.1.1 performReceiveLocked
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser)
            throws RemoteException {
        if (app != null) { // 进程存在
            if (app.thread != null) {
                try {
                    // 完成 AMS 跨进程到客户端的调用
                    app.thread.scheduleRegisteredReceiver(receiver, 
                    intent, resultCode, data, extras, ordered, sticky,
                    sendingUser, app.getReportedProcState());
                } catch (RemoteException 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);
        }
    }

ActivityThread.java

4.4.1.2 ActivityThread#ApplicationThread.scheduleRegisteredReceiver
public void scheduleRegisteredReceiver(IIntentReceiver receiver,
    Intent intent, int resultCode, String dataStr, Bundle extras,
    boolean ordered, boolean sticky, int sendingUser,
    int processState) throws RemoteException {
   
    updateProcessState(processState, false);
    // 广播发送到具体的 receiver 中
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
            sticky, sendingUser);
}

最后调用到 LoadedApk.java 中的 ReceiverDispatcher 中,具体代码就不分析了,可以参考 Android四大组件系列7 Broadcast广播机制(上) 的第二节动态 BroadcastReceiver 注册,在这一节有详细介绍,以下是示意图。
在这里插入图片描述

4.4.2 静态receiver的递送

说完动态递送,我们再来看静态递送。对于静态 receiver,情况会复杂很多,因为静态 receiver 所从属的进程有可能还没有运行起来呢。此时 BroadcastRecord 节点中记录的子列表的节点是 ResolveInfo 对象。

do {
     final long now = SystemClock.uptimeMillis();
     // 从 mDispatcher 中获取有序广播列表中的一个 BroadcastRecord
     r = mDispatcher.getNextBroadcastLocked(now);
     if (r == null) {
         ......
         return; // 如果为空,则返回
     }
     ......
     // 如果广播对应的 receiver 为空,或者其它条件成立,则跳过这次循环
     if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
          ......
          r = null;
          continue;
     }
} while (r == null);

 // Get the next receiver...
 int recIdx = r.nextReceiver++;

 // Keep track of when this receiver started, and make sure there
 // is a timeout message pending to kill it if need be.
 r.receiverTime = SystemClock.uptimeMillis();
 ......
 final Object nextReceiver = r.receivers.get(recIdx);
 ......
 // 静态广播 receiver 是 ResolveInfo 形式的
 ResolveInfo info = (ResolveInfo)nextReceiver;
 ComponentName component = new ComponentName(
        info.activityInfo.applicationInfo.packageName,
        info.activityInfo.name);
 ......
 // 获取目标 receiver 所在的进程信息
 String targetProcess = info.activityInfo.processName;
 ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
        info.activityInfo.applicationInfo.uid, false);
 ......
 if (skip) { // 如果skip了,则调用 scheduleBroadcastsLocked 执行下次消息循环
     ......
     scheduleBroadcastsLocked();
     return;
 }
 ......
 // 广播发送前的赋值操作
 r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
 r.state = BroadcastRecord.APP_RECEIVE;
 r.curComponent = component;
 r.curReceiver = info.activityInfo;
 ......
 // Broadcast is being executed, its package can't be stopped.
 try {
     AppGlobals.getPackageManager().setPackageStoppedState(
         r.curComponent.getPackageName(), false, r.userId);
 } catch (RemoteException e) {
 }
 ......

 // Is this receiver's application already running?
 // 静态 receiver 所在进程是 runnting 状态
 if (app != null && app.thread != null && !app.killed) {
    try {
       app.addPackage(info.activityInfo.packageName,
             info.activityInfo.applicationInfo.longVersionCode, 
             mService.mProcessStats);
                maybeAddAllowBackgroundActivityStartsToken(app, r);
                // 关键点1: 静态 receiver 广播的发送
                processCurBroadcastLocked(r, app, skipOomAdj);
                return;
     } catch (RemoteException e) {
                Slog.w(TAG, "Exception when sending broadcast to "
                      + r.curComponent, e);
     } catch (RuntimeException e) {
                ......
                return;
     }
 }

 // 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);
 // 关键点2:调用 mService.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) {
     ......
     return;
 }

 maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
 mPendingBroadcast = r;
 mPendingBroadcastRecvIndex = recIdx;

如果目标进程已经存在了,那么 app.thread 肯定不为 null,直接调用 processCurBroadcastLocked() 即可,否则就需要启动新进程了。启动的过程是异步的,可能很耗时,所以要把 BroadcastRecord 节点记入 mPendingBroadcast。

4.4.2.1 processCurBroadcastLocked()
private final void processCurBroadcastLocked(BroadcastRecord r,
    ProcessRecord app, boolean skipOomAdj) throws RemoteException {
    ......

    r.receiver = app.thread.asBinder();
    r.curApp = app;
    app.curReceivers.add(r);
    app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
    ......
    // Tell the application to launch this receiver.
    r.intent.setComponent(r.curComponent);

    boolean started = false;
    try {
        ......
        // 跨进程调用到 ActivityThread 中
     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());
        ......
        started = true;
    } finally {
        ......
    }
}

其中 ActivityInfo info 参数,记录着目标 receiver 的信息。

ActivityThread.java

4.4.2.2 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 中
    ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
           sync, false, mAppThread.asBinder(), sendingUser);
    r.info = info;
    r.compatInfo = compatInfo;
    sendMessage(H.RECEIVER, r);
}

然后发送消息 H.RECEIVER,接收到此消息后,调用 handleReceiver((ReceiverData)msg.obj)

4.4.2.3 ActivityThread.handleReceiver
private void handleReceiver(ReceiverData data) {
        ......
        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();
            ......
            // 通过反射机制生成 receiver
            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) {
            ......
        }

        try {
            ......
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            // 回调 receiver 的 onReceive 函数
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) {
            ......
        } finally {
            sCurrentBroadcastIntent.set(null);
        }

        if (receiver.getPendingResult() != null) {
            data.finish();
        }
    }

handleReceiver 中,会运用反射机制,创建出 BroadcastReceiver 对象,而后回调该对象的 onReceive() 成员函数。

4.4.2.4 必要时启动新进程

现在我们回过头来看,在目标进程尚未启动的情况下,是如何完成递送的。刚刚我们已经看到调用 mService.startProcessLocked 的句子了,只要不出问题,目标进程成功启动后就会调用 AMS 的 attachApplication()。

有关 attachApplication() 的详情,请参考 Android系统启动系列7 进程创建流程,此处我们只需知道它里面又会调用 attachApplicationLocked() 函数。

AMS.java

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
    ......
    // Check if a next-broadcast receiver is in this process...
    if (!badApp && isPendingBroadcastProcessLocked(pid)) {
        try {
            didSomething |= sendPendingBroadcastsLocked(app);
            ......
        } catch (Exception e) {
                ......
        }
    }
    ......
}

它们的意思是,如果新启动的进程就是刚刚 mPendingBroadcast 所记录的进程的话,此时 AMS 就会执行 sendPendingBroadcastsLocked(app) 一句。

AMS.java

boolean sendPendingBroadcastsLocked(ProcessRecord app) {
        boolean didSomething = false;
        for (BroadcastQueue queue : mBroadcastQueues) {
            didSomething |= queue.sendPendingBroadcastsLocked(app);
        }
        return didSomething;
}

BroadcastQueue 的 sendPendingBroadcastsLocked() 函数如下:

public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
    boolean didSomething = false;
    final BroadcastRecord br = 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;
             // 发送广播,参考 #### 4.4.2.1
             processCurBroadcastLocked(br, app, false);
             didSomething = true;
        } catch (Exception e) {
             ......
        }
     }
     return didSomething;
}

可以看到,既然目标进程已经成功启动了,那么 mPendingBroadcast 就可以赋值为 null 了。接着调用 sendPendingBroadcastsLocked() 和前文刚刚阐述的 processCurBroadcastLocked(),其内再通过 app.thread.scheduleReceiver(),将语义发送到用户进程,完成真正的广播递送。这部分在上一小节已有阐述,这里就不多说了。

4.4.3 有序广播是如何循环起来的

我们知道,平行广播的循环很简单,只是在一个 while 循环里对每个动态 receiver 执行 deliverToRegisteredReceiverLocked() 即可。

而对有序广播来说,原则上每次 processNextBroadcast() 只会处理一个 BroadcastRecord 的一个 receiver 而已。当然,此时摘下的 receiver 既有可能是动态注册的,也有可能是静态的。

对于动态注册的 receiver,目标进程处理完广播之后,会间接调用 am.finishReceiver() 向 AMS 发出反馈,关于这一步,其实在前面罗列 ReceiverDispatcher 的 performReceive() 时已经出现过了,我们再列一下:

LoadApk.java
#ReceiverDispatcher##InnerReceiver

4.4.3.1 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 (rd != null) { // 调用到 ReceiverDispatcher 的 performReceive
        rd.performReceive(intent, resultCode, data, extras,
           ordered, sticky, sendingUser);
    } else {
        ......
    }
}

调用到 ReceiverDispatcher 的 performReceive

LoadApk.java
#ReceiverDispatcher

public void performReceive(Intent intent, int resultCode, String data,
     Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
     final Args args = new Args(intent, resultCode, data, extras, ordered,
          sticky, sendingUser);
        if (intent == null) {
                Log.wtf(TAG, "Null intent received");
        } else {
            ......
        }
        // 调用 args 的 getRunnable
        if (intent == null || !mActivityThread.post(args.getRunnable())) {
            ......
        }
}

调用 args 的 getRunnable,并 post 到 mActivityThread 的消息队列中执行

LoadApk.java
#ReceiverDispatcher##Args.getRunnable

public final Runnable getRunnable() {
    return () -> {
        final BroadcastReceiver receiver = mReceiver;
        final boolean ordered = mOrdered;
        ......
        final IActivityManager mgr = ActivityManager.getService();
        final Intent intent = mCurIntent;
        ......
        try {
            ClassLoader cl = mReceiver.getClass().getClassLoader();
            intent.setExtrasClassLoader(cl);
            intent.prepareToEnterProcess();
            setExtrasClassLoader(cl);
            // 给 receiver 设定 PendingResult
            receiver.setPendingResult(this);
            // 执行 receiver 的 onReceive 回调
            receiver.onReceive(mContext, intent);
        } catch (Exception e) {
             ......
        }

         // 因为上面已经设置 PendingResult 了,所以走到此分支,调用 finish()
        if (receiver.getPendingResult() != null) {
            finish();
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    };
}

因为 Args 继承自 BroadcastReceiver.PendingResult,所以最终的 finish 调用的是 PendingResult 的函数。

final class Args extends BroadcastReceiver.PendingResult {
    ......
}

BroadcastReceiver.java
#PendingResult.finish

public final void finish() {
     if (mType == TYPE_COMPONENT) {//代表是静态注册的广播
          final IActivityManager mgr = ActivityManager.getService();
          if (QueuedWork.hasPendingWork()) {
               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);
    }
}

需要说明的是只有有序广播才需要循环处理,这里的有序广播包括:发送给静态注册的 BroadcastReceiver 的广播和发送给动态注册的 BroadcastReceiver 的广播但是声明的是有序;而平行广播是不需要循环处理的。

上面代码分析如下:
静态注册的广播接收者:

  • 当 QueuedWork 工作未完成,即 SharedPreferences 写入磁盘的操作没有完成,则等待完成再执行 sendFinished 方法
  • 当 QueuedWork 工作已完成,则直接调用 sendFinished 方法

动态注册的广播接收者:

  • 当发送的是串行广播,则直接调用 sendFinished 方法

另外常量参数说明:

  • TYPE_COMPONENT:静态注册
  • TYPE_REGISTERED:动态注册
  • TYPE_UNREGISTERED:取消注册

sendFinished

public void sendFinished(IActivityManager am) {
        synchronized (this) {
            ......
            try {
                ......
                // mOrderedHint代表发送是否为串行广播
                if (mOrderedHint) {
                    am.finishReceiver(mToken, mResultCode, 
                    mResultData, mResultExtras,
                    mAbortBroadcast, mFlags);
                } else {
                // 并行广播, 但属于静态注册的广播, 仍然需要告知AMS
                // 注意这里的参数 mToken 指的是 IIntentReceiver
                // AMS 通过这个 token 才能找到对应的
                    // 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) {
            }
        }
}

代码中的 am.finishReceiver() 会通知 AMS,表示用户侧 receiver 已经处理好了,或者至少告一段落了,请 AMS 进行下一步动作。

4.4.3.1 静态注册的receiver的循环

而对于静态注册的 receiver,情况是类似的,最终也是调用 am.finishReceiver() 向 AMS 发出回馈的,只不过发起的动作是在 ActivityThread 的 handleReceiver() 动作中。前文已经列过这个函数了,大家注意下面的句子即可:

private void handleReceiver(ReceiverData data) {
        ......
        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();
            ......
            // 通过反射机制生成 receiver
            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) {
            ......
        }

        try {
            ......
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            // 回调 receiver 的 onReceive 函数
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) {
            ......
        } finally {
            sCurrentBroadcastIntent.set(null);
        }

        if (receiver.getPendingResult() != null) {
        // 调用 BroadcastReceiver.PendingResult 的 finish
        // 这里的 ReceiverData 继承自 BroadcastReceiver.PendingResult
            data.finish();
        }
    }

这个 BroadcastReceiver.PendingResult 的 finish 函数,上面已经详细说明了,在此不再赘述。我们接下来看 AMS 的 finishReceiver 函数

AMS.finishReceiver

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);
        ......
        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;
                }
                // 这里的 who 指的是 IIntentReceiver
                r = queue.getMatchingOrderedReceiver(who);
                if (r != null) {
                    doNext = r.queue.finishReceiverLocked(r, resultCode,
                        resultData, resultExtras, resultAbort, true);
                }
                if (doNext) {
                    r.queue.processNextBroadcastLocked(/*fromMsg=*/ false,
                    /*skipOomAdj=*/ true);
                }
                // updateOomAdjLocked() will be done here
                trimApplicationsLocked(
                OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
            }

        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

可以看到最后通过 r.queue.finishReceiverLocked 返回一个是否还有后续广播需要处理,如果有的话,则调用 processNextBroadcastLocked 继续处理下一个广播,如此实现有序广播的循环操作。

4.4.4 有序广播的timeout处理

因为 AMS 很难知道一次广播究竟能不能完全成功递送出去,所以它必须实现一种“时限机制”。

前文在阐述 broadcastIntentLocked() 时,提到过 new 一个 BroadcastRecord 节点,并把这个 BroadcastRecord 节点插入到一个 BroadcastQueue 里的“平行列表”或者“有序列表”中。不过当时我们没有太细说那个 BroadcastQueue,现在我们多加一点儿说明。

实际上系统中有两个 BroadcastQueue,一个叫做“前台广播队列”,另一个叫“后台广播队列”,在 AMS 里是这样定义的:

BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;

为什么要搞出两个队列呢?我认为这是因为系统对“广播时限”的要求不同导致的。

对于前台广播队列而言,它里面的每个广播必须在 10 秒之内把广播递送给 receiver,而后台广播队列的时限比较宽,只需 60 秒之内递送到就可以了。具体定义在 AMS 中。

static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

注意,这个 10 秒或 60 秒限制是针对一个 receiver 而言的。

比如说“前台广播队列”的某个 BroadcastRecord 节点对应了 3 个 receiver,那么在处理这个广播节点时,只要能在 30 秒(3 x 10)之内搞定就可以了。事实上,AMS 系统考虑了更多东西,所以它给一个 BroadcastRecord 的总时限是其所有 receiver 时限之和的 2 倍,在此例中就是 60 秒(2 x 3 x 10)。

对于平行 receiver 而言,不考虑时限,因为动态 receiver 是直接递送到目标进程的,它不考虑目标端是什么时候处理完这个广播的。

然而对于有序 receiver 来说,时限就比较重要了。因为 receiver 之间必须是串行处理的,也就是说上一个 receiver 在没处理完时,系统是不会让下一个 receiver 进行处理的。

从 processNextBroadcast() 的代码来看,在处理有序 receiver 时,BroadcastRecord 里的 nextReceiver 域会记录“下一个应该处理的 receiver” 的标号。

只有在 BroadcastRecord 的所有 receiver 都处理完后,或者 BroadcastRecord 的处理时间超过了总时限的情况下,系统才会把这个 BroadcastRecord 节点从队列里删除。因此我们在 processNextBroadcast() 里看到,通过 BroadcastDispatcher 获取当前 BroadcastRecord 的句子是写死为 mOrderedBroadcasts.remove(0) 的。
在这里插入图片描述
在拿到当前 BroadcastRecord 之后,利用 nextReceiver 值拿到当前该处理的 receiver 信息:

int recIdx = r.nextReceiver++;
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
    r.dispatchTime = r.receiverTime;
    r.dispatchClockTime = System.currentTimeMillis();
    ......
}
if (! mPendingBroadcastTimeoutMessage) {
// 这里的 mConstants.TIMEOUT 为 10 秒
    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 Object nextReceiver = r.receivers.get(recIdx);

当然,一开始,nextReceiver 的值只会是 0,表示第一个 receiver 有待处理,此时会给 BroadcastRecord 的 dispatchTime 域赋值。

也就是说,dispatchTime 的意义是标记实际处理 BroadcastRecord 的起始时间,那么这个 BroadcastRecord 所能允许的最大时限值就是:

dispatchTime + 2 * BROADCAST_FG_TIMEOUT / BROADCAST_BG_TIMEOUT * 其 receiver 总数

一旦超过这个时限,而 BroadcastRecord 又没有处理完,那么就强制结束这个 BroadcastRecord 节点:

int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) 
    if ((numReceivers > 0) && (now > r.dispatchTime + 
    (2 * mConstants.TIMEOUT * numReceivers))) {
    // 超时,强制结束这个广播的发送
        Slog.w(TAG, "Hung broadcast ["
             + mQueueName + "] discarded after timeout failure:"
             + " now=" + now
             + " dispatchTime=" + r.dispatchTime
             + " startTime=" + r.receiverTime
             + " intent=" + r.intent
             + " numReceivers=" + numReceivers
             + " nextReceiver=" + r.nextReceiver
             + " state=" + r.state);
        // forcibly finish this broadcast
        broadcastTimeoutLocked(false); 
        forceReceive = true;
        r.state = BroadcastRecord.IDLE;
    }
}

此处调用的 broadcastTimeoutLocked() 的参数是 boolean fromMsg,表示这个函数是否是在处理“时限消息”的地方调用的,因为当前是在 processNextBroadcast() 函数里调用 broadcastTimeoutLocked() 的,所以这个参数为 false。从这个参数也可以看出,另一处判断“处理已经超时”的地方是在消息处理机制里,在那个地方,fromMsg 参数应该设为 true。

大体上说,每当 processNextBroadcast() 准备递送 receiver 时,会调用 setBroadcastTimeoutLocked() 设置一个延迟消息,setBroadcastTimeoutLocked() 的代码如下:

final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
     }
}

只要我们的 receiver 能及时处理广播,系统就会 cancel 上面的延迟消息。这也就是说,但凡事件的 handleMessage() 开始处理这个消息,就说明 receiver 处理超时了。此时,系统会放弃处理这个receiver,并接着尝试处理下一个 receiver。

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

接着看 broadcastTimeoutLocked

final void broadcastTimeoutLocked(boolean fromMsg) {
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }
    ......
    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
    if (fromMsg) {
        ......
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        if (timeoutTime > now) {
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                 "Premature timeout ["
                 + mQueueName + "] @ " + now + 
                 + ": resetting BROADCAST_TIMEOUT_MSG for "
                 + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
                return;
        }
    }

    if (r.state == BroadcastRecord.WAITING_SERVICES) {
        Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
             ? r.curComponent.flattenToShortString() : "(null)"));
        r.curComponent = null;
        r.state = BroadcastRecord.IDLE;
        processNextBroadcast(false);
        return;
    }
    
    final boolean debugging = (r.curApp != null &&
        r.curApp.isDebugging());

    Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
           + ", started " + (now - r.receiverTime) + "ms ago");
    r.receiverTime = now;
    if (!debugging) {
        r.anrCount++;
    }

    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;
    }
    Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
    logBroadcastReceiverDiscardLocked(r);
    if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
         BroadcastFilter bf = (BroadcastFilter)curReceiver;
         if (bf.receiverList.pid != 0
              && bf.receiverList.pid != ActivityManagerService.MY_PID) {
              synchronized (mService.mPidsSelfLocked) {
                 app = mService.mPidsSelfLocked.get(
                      bf.receiverList.pid);
              }
         }
    } else {
         app = r.curApp;
    }

    if (app != null) {
        anrMessage = "Broadcast of " + r.intent.toString();
    }

    if (mPendingBroadcast == r) {
         mPendingBroadcast = null;
    }

    // Move on to the next receiver.
    // 结束本次 receiver 处理
   finishReceiverLocked(r, r.resultCode, r.resultData,
          r.resultExtras, r.resultAbort, false);
   // 继续下一个 receiver 的处理
   scheduleBroadcastsLocked();

   if (!debugging && anrMessage != null) {
       // 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));
   }
}
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;
}

可以看到,当一个 receiver 超时后,系统会放弃继续处理它,并再次调用 scheduleBroadcastsLocked(),然后调用 processNextBroadcast 尝试处理下一个 receiver。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值