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

你可能感兴趣的:(Android,framework)