亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ActivityManagerService廣播并行發送與串行發送怎么實現

發布時間:2023-03-02 11:29:36 來源:億速云 閱讀:113 作者:iii 欄目:開發技術

這篇文章主要講解了“ActivityManagerService廣播并行發送與串行發送怎么實現”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“ActivityManagerService廣播并行發送與串行發送怎么實現”吧!

"并行"廣播的發送

本文以 ActivityManagerService之廣播(1): 注冊與發送 為基礎,分析“串行”和“并行”廣播的發送流程,并介紹廣播 ANR 的原理。

// 1. 獲取廣播隊列
final BroadcastQueue queue = broadcastQueueForIntent(intent);
// 2. 創建廣播記錄
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
        callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
        requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers,
        resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId,
        allowBackgroundActivityStarts, backgroundActivityStartsToken,
        timeoutExempt);
// 3. 廣播記錄加入到并行隊列中
queue.enqueueParallelBroadcastLocked(r);
// 4. 調度發送廣播
queue.scheduleBroadcastsLocked();

第3步,把廣播記錄保存到并行隊列中

// BroadcastQueue.java
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
    // mParallelBroadcasts 類型為 ArrayList<BroadcastRecord>
    mParallelBroadcasts.add(r);
    enqueueBroadcastHelper(r);
}

第4步,調度發送廣播,最終會調用如下方法

// BroadcastQueue.java
// 此時,參數 fromMsg 為 true,skipOomAdj 為 false
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    mService.updateCpuStats();
    if (fromMsg) {
        mBroadcastsScheduled = false;
    }
    // 遍歷"并行"廣播隊列
    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);
    }
    // ... 省略"串行"廣播的發送 ...
}

雖然名為“并行”廣播,但是仍然是從隊列取出廣播,然后逐個發送給動態接收器。很顯然,這里的行為與“并行”的含義并不一致?那么廣播的“并行”發送究竟是什么意思?

接著看“并行”廣播如何發送給動態接收器的

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
        BroadcastFilter filter, boolean ordered, int index) {
    // ... 省略一大堆的權限或者異常檢測 ...
    // 一個廣播可能有多個接收者,因此需要一個數組來保存發送的狀態
    r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
    // ordered 目前為 false
    if (ordered) {
        // ...
        }
    } else if (filter.receiverList.app != null) {
        // 馬上要發送廣播給接收方,因此要暫時解凍接收方的進程
        mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app);
    }
    try {
        if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
            // ... 處于備份狀態中 ...
        } else {
            r.receiverTime = SystemClock.uptimeMillis();
            // 保存允許從后臺啟動activity的token
            maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
            // 添加到省電模式白名單中
            maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
            // 執行廣播的發送
            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 (filter.receiverList.app != null
                    && r.allowBackgroundActivityStarts && !r.ordered) {
                postActivityStartTokenRemoval(filter.receiverList.app, r);
            }
        }
        // ordered 目前為 false
        if (ordered) {
            r.state = BroadcastRecord.CALL_DONE_RECEIVE;
        }
    } catch (RemoteException e) {
        // ...
    }
}

拋開一些細節,直接看 performReceiveLocked()

// BroadcastQueue.java
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) {
        final IApplicationThread thread = app.getThread();
        if (thread != null) {
            try {
                // 發送廣播給接收方進程
                thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser,
            } catch (RemoteException ex) {
               // ...
            }
        } else {
            throw new RemoteException("app.thread must not be null");
        }
    } else {
        // ...
    }
}

很簡單,就是通過進程 attach 的 IApplicationThread 接口,發送廣播給進程。這個過程,暫時先不分析,后面會分析到。

那么,現在來回答一下,何為“并行”廣播?其實這個答案,我也是對比了串行廣播的發送過程,才得出來的。所謂的"并行"發送,實際上就是把廣播逐個發送給動態接收器,但是不需要等待前一個接收器反饋處理結果,就可以發送下一個。而“串行”廣播的發送,是需要等待前一個廣播接收器反饋處理結果后,才能調度發送下一個廣播。

“串行”廣播的發送

// 1.獲取廣播隊列
BroadcastQueue queue = broadcastQueueForIntent(intent);
// 2.創建廣播記錄
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);
// 3.廣播記錄加入到串行隊列中
queue.enqueueOrderedBroadcastLocked(r);
// 4.調度發送廣播
queue.scheduleBroadcastsLocked();

第3步,廣播加入到串行隊列中

// BroadcastQueue.java
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
    mDispatcher.enqueueOrderedBroadcastLocked(r);
    enqueueBroadcastHelper(r);
}
// BroadcastDispatcher.java
void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
    mOrderedBroadcasts.add(r);
}

并行發送的廣播保存到 BroadcastQueue#mParallelBroadcasts 中,而串行發送的廣播保存到 BroadcastDispatcher#mOrderedBroadcasts 中,為何要這樣設計呢?有興趣的讀者可以研究下。

第4步,“串行”廣播的調度發送,仍然使用的是 processNextBroadcastLocked() 方法,但是代碼量是非常的大,下面將把函數分段解析。

processNextBroadcastLocked() 函數有400多行代碼,這個函數里有很多東西都可以抽出來的,但是隨著版本的更新,這塊代碼一直沒有優化過。

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    mService.updateCpuStats();
    if (fromMsg) {
        mBroadcastsScheduled = false;
    }
    // 把并行廣播發送給動態接收器
    while (mParallelBroadcasts.size() > 0) {
        // ...
    }
    // 1. 處理 receiver 進程正在啟動的情況
    if (mPendingBroadcast != null) {
        // 檢測 receiver 進程是否死亡
        boolean isDead;
        if (mPendingBroadcast.curApp.getPid() > 0) {
            synchronized (mService.mPidsSelfLocked) {
                ProcessRecord proc = mService.mPidsSelfLocked.get(
                        mPendingBroadcast.curApp.getPid());
                isDead = proc == null || proc.mErrorState.isCrashing();
            }
        } else {
            final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
                    mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
            isDead = proc == null || !proc.isPendingStart();
        }
        if (!isDead) {
            // 進程仍然存活,結束此次廣播的處理流程,繼續等待
            // 等待什么呢?等待廣播進程起來,并與 AMS 完成 attach application
            // 在 attach application 的過程中,會完成廣播的發送
            return;
        } else {
            // 進程死亡,繼續處理下一個廣播
            mPendingBroadcast.state = BroadcastRecord.IDLE;
            mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
            mPendingBroadcast = null;
        }
    }

當發送一個廣播給 receiver 時,如果 receiver 進程沒有啟動,那么會先 fork 一個 receiver 進程,然后用 mPendingBroadcast 保存待發送的廣播。當 receiver 進程起來的時候,會與 AMS 執行 attach application 過程,在這個過程中,會自動把 mPendingBroadcast 保存的廣播發送給 receiver 進程。

因此,這里檢測到 mPendingBroadcast 不為 null 時,那么 receiver 進程肯定在啟動中,只要 receiver 進程沒有死亡,就什么也不用做,因為廣播會自動發送給 receiver 進程。

接著看下一步的處理

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    // ...
    // 把并行廣播發送給動態接收器
    while (mParallelBroadcasts.size() > 0) {
        // ...
    }
    // 1. 處理 receiver 進程正在啟動的情況
    if (mPendingBroadcast != null) {
        // ...
    }
    boolean looped = false;
    // 2. 通過 do-while 循環,找到一個現在可以處理的廣播
    do {
        final long now = SystemClock.uptimeMillis();
        // 獲取一個待處理的廣播
        r = mDispatcher.getNextBroadcastLocked(now);
        if (r == null) {
            // ... 沒有廣播需要處理 ...
            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))) {
                broadcastTimeoutLocked(false); // forcibly finish this broadcast
                forceReceive = true;
                r.state = BroadcastRecord.IDLE;
            }
        }
        if (r.state != BroadcastRecord.IDLE) {
            return;
        }
        // 當前廣播因為某種原因,終止處理,然后處理下一個廣播 
        if (r.receivers == null || r.nextReceiver >= numReceivers
                || r.resultAbort || forceReceive) {
            // ...
            // 通知 BroadcastDispatcher ,不處理這個廣播了
            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)) {
                // ...
                // 保存推遲發送的廣播
                mDispatcher.addDeferredBroadcast(receiverUid, defer);
                r = null;
                looped = true;
                // 下一次循環時,獲取下一個廣播來處理
                continue;
            }
        }
    } while (r == null);

先從整體看,通過一個 do-while 循環,最終是為了找到下一個處理的廣播。為何要用一個循環來尋找呢? 因為廣播可能沒有接收器,或者已經嚴重超時,又或者廣播需要推遲發送。所以要通過一個循環,找到一個能立即發送的廣播。

由于本文主要是為了分析廣播發送的整體流程,對于有些細節,只做注釋而不做細致分析。需要深入研究的讀者,可以在本文的基礎上繼續分析。

繼續接著看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    mService.updateCpuStats();
    if (fromMsg) {
        mBroadcastsScheduled = false;
    }
    // 把并行廣播發送給動態接收器
    while (mParallelBroadcasts.size() > 0) {
        // ...
    }
    // 1. 處理 receiver 進程正在啟動的情況
    if (mPendingBroadcast != null) {
        // ...
    }
    boolean looped = false;
    // 2. 通過 do-while 循環,找到一個現在可以處理的廣播
    do {
        final long now = SystemClock.uptimeMillis();
        // 獲取一個待處理的廣播
        r = mDispatcher.getNextBroadcastLocked(now);
        // ...
    } while (r == null);
    // 走到這里,表示已經獲取了一個現在可以處理的廣播
    int recIdx = r.nextReceiver++;
    // 3. 在發送廣播之前,先發送一個超時消息
    r.receiverTime = SystemClock.uptimeMillis();
    if (recIdx == 0) {
        // 在廣播開始發送給第一個接收器時,記錄發送的時間
        r.dispatchTime = r.receiverTime;
        r.dispatchClockTime = System.currentTimeMillis();
    }
    if (! mPendingBroadcastTimeoutMessage) {
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        setBroadcastTimeoutLocked(timeoutTime);
    }

在廣播發送給一個 receiver 之前,會先發送一個超時消息。從廣播準備發送給一個 receiver 算起,到 receiver 處理完廣播,并反饋給 AMS,如果這個時間段超過了一個時間閾值,就會引發 ANR。觸發 ANR 的代碼設計非常巧妙,后面會具體分析這個過程。

接著看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    // ...
    // 把并行廣播發送給動態接收器
    while (mParallelBroadcasts.size() > 0) {
        // ...
    }
    // 1. 處理 receiver 進程正在啟動的情況
    if (mPendingBroadcast != null) {
        // ...
    }
    boolean looped = false;
    // 2. 通過 do-while 循環,找到一個現在可以處理的廣播
    do {
        // ...
    } while (r == null);
    int recIdx = r.nextReceiver++;
    // ...
    // 3. 在發送廣播之前,先發送一個超時消息
    // 當廣播處理超時時,會觸發 ANR
    if (! mPendingBroadcastTimeoutMessage) {
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        setBroadcastTimeoutLocked(timeoutTime);
    }
    final BroadcastOptions brOptions = r.options;
    // 4. 獲取一個 receiver
    final Object nextReceiver = r.receivers.get(recIdx);
    // 5. 如果這個接收器是動態接收器,先把廣播發送給它
    // 注意,這里處理的是有序廣播發送給動態接收器的情況
    if (nextReceiver instanceof BroadcastFilter) {
        BroadcastFilter filter = (BroadcastFilter)nextReceiver;
        // 發送廣播給動態接收器
        deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
        if (r.receiver == null || !r.ordered) {
            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);
            }
        }
        // 注意,把廣播發送給 動態receiver 后,直接返回
        return;
    }

現在一切就緒,那么開始獲取一個 receiver,當這個 receiver 是一個動態接收器時,直接發送廣播給它,這個發送過程前面已經分析過。

注意,這里處理的情況是,把有序廣播發送給動態接收器。并且發送完成后,直接 return, 也就是結束了此次廣播的發送流程。

一個廣播可能有多個接收器,為何這里只發送給一個動態接收器,就直接返回了? 這就是從“串行”廣播的本質,需要等待當前的廣播接收器處理完廣播,并返回結果后,才能把廣播發送給下一個廣播接收器。

接著看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    // ...
    // 把并行廣播發送給動態接收器
    while (mParallelBroadcasts.size() > 0) {
        // ...
    }
    // 1. 處理 receiver 進程正在啟動的情況
    if (mPendingBroadcast != null) {
        // ...
    }
    boolean looped = false;
    // 2. 通過 do-while 循環,找到一個現在可以處理的廣播
    do {
        final long now = SystemClock.uptimeMillis();
        // 獲取一個待處理的廣播
        r = mDispatcher.getNextBroadcastLocked(now);
        // ...
    } while (r == null);
    // 走到這里,表示已經獲取了一個現在可以處理的廣播
    int recIdx = r.nextReceiver++;
    // 3. 在發送廣播之前,先發送一個超時消息
    r.receiverTime = SystemClock.uptimeMillis();
    // ...
    if (! mPendingBroadcastTimeoutMessage) {
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        setBroadcastTimeoutLocked(timeoutTime);
    }
    final BroadcastOptions brOptions = r.options;
    // 4. 獲取一個 receiver
    final Object nextReceiver = r.receivers.get(recIdx);
    // 5. 如果這個接收器是動態接收器,先把廣播發送給它
    // 注意,這里處理的是有序廣播發送給動態接收器的情況
    if (nextReceiver instanceof BroadcastFilter) {
        // ...
        return;
    }
    // 走到這里,表示當前的廣播接收器,是靜態接收器
    // 獲取靜態接收器的信息
    ResolveInfo info =
        (ResolveInfo)nextReceiver;
    ComponentName component = new ComponentName(
            info.activityInfo.applicationInfo.packageName,
            info.activityInfo.name);
    boolean skip = false;
    // 6. 檢測是否不需要把廣播發送給靜態接收器
    // ... 省略一大堆的檢測代碼 ...
    String targetProcess = info.activityInfo.processName;
    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
            info.activityInfo.applicationInfo.uid);
    if (!skip) {
        // 檢測是否允許把廣播發送給靜態接收器
        final int allowed = mService.getAppStartModeLOSP(
                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
                info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
        // 例如,大于等于 O+ 版本的 app ,不允許廣播發送給靜態接收器
        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
            // ephemeral app 會返回這個模式
            if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                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());
                skip = true;
            }
        }
    }
    // 跳過當前廣播的發送
    if (skip) {
        // ...
        return;
    }

如果這個 reciever 是靜態接收器,那么在把廣播發送給它之前,首先得進行一大堆的檢測。最常見的就是權限,但是這里展示了一段 Android O+ 限制廣播發送給靜態接收器的限制,有興趣的讀者可以詳細分析。

接著看下一步

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    // ...
    // 把并行廣播發送給動態接收器
    while (mParallelBroadcasts.size() > 0) {
        // ...
    }
    // 1. 處理 receiver 進程正在啟動的情況
    if (mPendingBroadcast != null) {
        // ...
    }
    boolean looped = false;
    // 2. 通過 do-while 循環,找到一個現在可以處理的廣播
    do {
        final long now = SystemClock.uptimeMillis();
        // 獲取一個待處理的廣播
        r = mDispatcher.getNextBroadcastLocked(now);
        // ...
    } while (r == null);
    // 走到這里,表示已經獲取了一個現在可以處理的廣播
    int recIdx = r.nextReceiver++;
    // 3. 在發送廣播之前,先發送一個超時消息
    r.receiverTime = SystemClock.uptimeMillis();
    // ...
    if (! mPendingBroadcastTimeoutMessage) {
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        setBroadcastTimeoutLocked(timeoutTime);
    }
    final BroadcastOptions brOptions = r.options;
    // 4. 獲取一個 receiver
    final Object nextReceiver = r.receivers.get(recIdx);
    // 5. 如果這個接收器是動態接收器,先把廣播發送給它
    // 注意,這里處理的是有序廣播發送給動態接收器的情況
    if (nextReceiver instanceof BroadcastFilter) {
        // ...
        return;
    }
    // 走到這里,表示當前的廣播接收器,是靜態接收器
    ResolveInfo info =
        (ResolveInfo)nextReceiver;
    ComponentName component = new ComponentName(
            info.activityInfo.applicationInfo.packageName,
            info.activityInfo.name);
    boolean skip = false;
    // 6. 檢測是否不需要把廣播發送給靜態接收器
    // ... 省略一大堆的檢測代碼 ...
    String targetProcess = info.activityInfo.processName;
    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
            info.activityInfo.applicationInfo.uid);
    // ...
    // 跳過當前廣播的發送
    if (skip) {
        // ...
        return;
    }
    // 現在可以把廣播發送給靜態接收器了
    // ...
    // 7. 靜態接收器的進程正在運行,那么就把廣播發送給它
    if (app != null && app.getThread() != null && !app.isKilled()) {
        try {
            app.addPackage(info.activityInfo.packageName,
                    info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
            maybeAddAllowBackgroundActivityStartsToken(app, r);
            // 發送廣播給廣播進程
            processCurBroadcastLocked(r, app);
            // 注意,廣播發送給這個靜態接收器后,直接結束此次廣播的處理
            return;
        } catch (RemoteException e) {
            // ...
        }
    }
}

如果沒有限制,那么現在就可以把廣播發送給靜態接收器。

如果靜態接收器所在的進程已經運行了,那么把廣播發送給這個進程,這個過程與前面發送廣播給動態接收器的過程非常類似,這里就不分析了。

注意,這里把廣播發送給一個靜態接收器,也是直接 return,懂了吧?

接著往下看

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    // ...
    // 把并行廣播發送給動態接收器
    while (mParallelBroadcasts.size() > 0) {
        // ...
    }
    // 1. 處理 receiver 進程正在啟動的情況
    if (mPendingBroadcast != null) {
        // ...
    }
    boolean looped = false;
    // 2. 通過 do-while 循環,找到一個現在可以處理的廣播
    do {
        final long now = SystemClock.uptimeMillis();
        // 獲取一個待處理的廣播
        r = mDispatcher.getNextBroadcastLocked(now);
        // ...
    } while (r == null);
    // 走到這里,表示已經獲取了一個現在可以處理的廣播
    int recIdx = r.nextReceiver++;
    // 3. 在發送廣播之前,先發送一個超時消息
    r.receiverTime = SystemClock.uptimeMillis();
    // ...
    if (! mPendingBroadcastTimeoutMessage) {
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        setBroadcastTimeoutLocked(timeoutTime);
    }
    final BroadcastOptions brOptions = r.options;
    // 4. 獲取一個 receiver
    final Object nextReceiver = r.receivers.get(recIdx);
    // 5. 如果這個接收器是動態接收器,先把廣播發送給它
    // 注意,這里處理的是有序廣播發送給動態接收器的情況
    if (nextReceiver instanceof BroadcastFilter) {
        // ...
        return;
    }
    // 走到這里,表示當前的廣播接收器,是靜態接收器
    ResolveInfo info =
        (ResolveInfo)nextReceiver;
    ComponentName component = new ComponentName(
            info.activityInfo.applicationInfo.packageName,
            info.activityInfo.name);
    boolean skip = false;
    // 6. 檢測是否不需要把廣播發送給靜態接收器
    // ...
    // 跳過當前廣播的發送
    if (skip) {
        r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
        r.receiver = null;
        r.curFilter = null;
        r.state = BroadcastRecord.IDLE;
        r.manifestSkipCount++;
        // 發送下一個廣播
        scheduleBroadcastsLocked();
        return;
    }
    // 現在可以把廣播發送給靜態接收器了
    // ...
    // 7. 靜態接收器的進程正在運行,那么就把廣播發送給它
    if (app != null && app.getThread() != null && !app.isKilled()) {
        // ...
    }
    // 8. 靜態接收器的進程沒有運行,fork it!
    r.curApp = mService.startProcessLocked(targetProcess,
            info.activityInfo.applicationInfo, true,
            r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
            new HostingRecord("broadcast", r.curComponent), isActivityCapable
            ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
            (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
    // 處理 fork 進程失敗的情況
    if (r.curApp == null) {
        // ...
        return;
    }
    maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
    // fork 進程成功,保存廣播數據,等待進程起來后,再處理這個廣播
    mPendingBroadcast = r;
    mPendingBroadcastRecvIndex = recIdx;
}

剛才已經處理了靜態接收器的進程存在的情況,那么現在處理進程不存在的情況,因此首先得 fork 進程。當成功 fork 進程后,保存待發送的廣播的數據,例如,用 mPendingBroadcast 保存廣播,然后當進程啟動時,與 AMS 進行 attach application 時,會自動把廣播發送給該進程。這個過程后面會分析。

注意,此時函數已經結束,而廣播正在發送給一個正在啟動的進程。很顯然,需要等待這個廣播的處理結果,才能繼續下一個廣播的發送,這也符合“串行”廣播的定義。

廣播發送給正在啟動的進程

剛才,我們分析到一個過程,當靜態接收器所在的進程沒有啟動的時候,首先 fork 進程,那么廣播之后是如何發送給進程的呢?

首先,我們知道當進程啟動后,會執行 attach application 過程,最終會調用 AMS 如下方法

// ActivityManagerService.java
    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        // ...
        try {
            // ...
            if (app.getIsolatedEntryPoint() != null) {
                // ...
            } else if (instr2 != null) {
                // ...
            } else {
                // 初始化進程環境
                thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.getCompat(), getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions,
                        app.getDisabledCompatChanges(), serializedSystemFontMap);
            }
            // ...
        } catch (Exception e) {
            // ...
        }
        // ....
        // 處理正在等待宿主進程起來的廣播
        if (!badApp && isPendingBroadcastProcessLocked(pid)) {
            try {
                // 發送隊列中正在等待進程起來的廣播
                didSomething |= sendPendingBroadcastsLocked(app);
                checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
            } catch (Exception e) {
                // ...
            }
        }
        // ...
        return true;
    }
    boolean sendPendingBroadcastsLocked(ProcessRecord app) {
        boolean didSomething = false;
        for (BroadcastQueue queue : mBroadcastQueues) {
            didSomething |= queue.sendPendingBroadcastsLocked(app);
        }
        return didSomething;
    }

看到了,AMS 首先對進程進行了初始化,然后就會把等待進程啟動的廣播,發送給它。

// BroadcastQueue.java
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
    boolean didSomething = false;
    // mPendingBroadcast 保存的就是等待進程啟動啟動后,需要發送的廣播。
    final BroadcastRecord br = mPendingBroadcast;
    if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
        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;
            // 發送廣播給進程
            processCurBroadcastLocked(br, app);
            didSomething = true;
        } catch (Exception e) {
            // ...
        }
    }
    return didSomething;
}

mPendingBroadcast 保存的就是等待進程啟動啟動后,需要發送的廣播。現在進程已經啟動,立即發送廣播

// BroadcastQueue.java
private final void processCurBroadcastLocked(BroadcastRecord r,
        ProcessRecord app) throws RemoteException {
    final IApplicationThread thread = app.getThread();
    if (thread == null) {
        throw new RemoteException();
    }
    if (app.isInFullBackup()) {
        skipReceiverLocked(r);
        return;
    }
    // 更新正在處理廣播的 receiver 數據
    r.receiver = thread.asBinder();
    r.curApp = app;
    // 保存當前正在運行的 receiver
    final ProcessReceiverRecord prr = app.mReceivers;
    prr.addCurReceiver(r);
    app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
    mService.updateLruProcessLocked(app, false, null);
    mService.enqueueOomAdjTargetLocked(app);
    mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
    r.intent.setComponent(r.curComponent);
    boolean started = false;
    try {
        mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
                                  PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
        // 通知進程啟動 receiver 來處理廣播
        thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
                r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                app.mState.getReportedProcState());
        started = true;
    } finally {
        if (!started) {
            // ...
        }
    }
}

現在 AMS 通知 receiver 所在的進程來處理廣播

// ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
    private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";
    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);
    }

最終調用 handleReceiver() 處理廣播數據

private void handleReceiver(ReceiverData data) {
    unscheduleGcIdler();
    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 {
        // 1. 創建 Application 對象,并調用 Application#onCreate()
        app = packageInfo.makeApplication(false, mInstrumentation);
        // ...
        // 2. 創建 BroadcastReceiver 對象
        receiver = packageInfo.getAppFactory()
                .instantiateReceiver(cl, data.info.name, data.intent);
    } catch (Exception e) {
        // ...
    }
    try {
        sCurrentBroadcastIntent.set(data.intent);
        receiver.setPendingResult(data);
        // 3. 執行 BroadcastReceiver#onReceive()
        receiver.onReceive(context.getReceiverRestrictedContext(),
                data.intent);
    } catch (Exception e) {
        // ...
    } finally {
        sCurrentBroadcastIntent.set(null);
    }
    // 4. 返回廣播的處理結果給 AMS
    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}

這里的過程很清晰明了吧,直接看最后一步,把廣播的處理結果反饋給 AMS

// BroadcastReceiver.java
public final void finish() {
    if (mType == TYPE_COMPONENT) {
        final IActivityManager mgr = ActivityManager.getService();
        if (QueuedWork.hasPendingWork()) {
            // ...
        } else {
            sendFinished(mgr);
        }
    } else if (mOrderedHint &amp;&amp; mType != TYPE_UNREGISTERED) {
        // ...
    }
}
public void sendFinished(IActivityManager am) {
    synchronized (this) {
        if (mFinished) {
            throw new IllegalStateException("Broadcast already finished");
        }
        mFinished = true;
        try {
            if (mResultExtras != null) {
                mResultExtras.setAllowFds(false);
            }
            if (mOrderedHint) {
                // 有序廣播的反饋
                am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                        mAbortBroadcast, mFlags);
            } else {
                // 非有序廣播的費奎
                am.finishReceiver(mToken, 0, null, null, false, mFlags);
            }
        } catch (RemoteException ex) {
        }
    }
}

現在看下 AMS 如何處理這個反饋的結果

// ActivityManagerService.java
public void finishReceiver(IBinder who, int resultCode, String resultData,
        Bundle resultExtras, boolean resultAbort, int flags) {
    // ...
    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;
            }
            // 1. 匹配進程正在處理的廣播
            r = queue.getMatchingOrderedReceiver(who);
            // 2. 完成當前 receiver 廣播的處理流程
            if (r != null) {
                doNext = r.queue.finishReceiverLocked(r, resultCode,
                    resultData, resultExtras, resultAbort, true);
            }
            // 3. 發送廣播給下一個 receiver,或者發送下一個廣播
            if (doNext) {
                r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
            }
            // updateOomAdjLocked() will be done here
            trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

看到了,只有當前 receiver 處理完廣播,才會發送廣播給下一個 receiver,這就是“串行”廣播的本質。

廣播 ANR

最后,來探討一個廣播 ANR 的原理,本來我以為很簡單的,就是發送一個超時消息嘛。但是當我細看的時候,我發現這個 ANR 設計的很巧妙,我覺得我們可以學習下,因此這里單獨拿出來分析。

這里,我得提醒大家一點,只有“串行”廣播才會發生 ANR,因為它要等待 receiver 的處理結果。

根據前面分析,“串行”廣播發送給 receiver 前,會發送一個超時消息,如下

// BroadcastQueue.java
if (! mPendingBroadcastTimeoutMessage) {
    long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
    setBroadcastTimeoutLocked(timeoutTime);
}

當這個消息被執行的時候,會調用如下代碼

// BroadcastQueue.java
final void broadcastTimeoutLocked(boolean fromMsg) {
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }
    if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
        return;
    }
    long now = SystemClock.uptimeMillis();
    // 獲取當前正在處理的廣播
    BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
    if (fromMsg) {
        // 系統還沒有就緒
        if (!mService.mProcessesReady) {
            return;
        }
        // 廣播超時被豁免
        if (r.timeoutExempt) {
            if (DEBUG_BROADCAST) {
                Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
                        + r.intent.getAction());
            }
            return;
        }
        // 1. 廣播沒有超時
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        if (timeoutTime > now) {
            // 發送下一個超時消息
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
    }
    if (r.state == BroadcastRecord.WAITING_SERVICES) {
        // ...
        return;
    }
    // 2. 走到這里,表示廣播超時,觸發 ANR
    final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
    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;
    }
    logBroadcastReceiverDiscardLocked(r);
    // 獲取 receiver 進程
    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;
    }
    // 強制結束當前廣播的發送流程
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, false);
    // 調度下一次的廣播發送
    scheduleBroadcastsLocked();
    // app 不處于 debug 模式,引發 ANR
    if (!debugging && anrMessage != null) {
        mService.mAnrHelper.appNotResponding(app, anrMessage);
    }
}

第2步,引發 ANR ,很簡單,就是因為整個發送與反饋過程超時了。

而第1步,就是處理不超時的情況。這里大家是不是很疑惑,移除超時消息不是在接收到廣播反饋后進行的嗎? 我可以負責地告訴你,并不是!那這里第1步怎么理解呢?

首先這個超時消息一定觸發,但是觸發這個超時消息,并不代表一定會引發 ANR。

假如當前 receiver 的廣播處理流程,在超時時間之前就完成了,那么 AMS 會調度廣播發送給下一個 receiver。

于是,針對下一個 receiver ,會更新 r.receiverTime,那么第一步此時計算出來的 timeoutTime 是下一個 receiver 的廣播超時時間,很顯然是大于 now 的,于是就不會走第2步的 ANR 流程。

最后利用這個 timeoutTime,為下一個 receiver 再發送一個超時消息,簡直是完美!

至于為何不在廣播反饋的時候,移除這個超時消息,我心中有一點小小的想法,但是也不能確定是不是這個原因,才這樣設計的。不過,對我來說,這一招,我算是學會了。

感謝各位的閱讀,以上就是“ActivityManagerService廣播并行發送與串行發送怎么實現”的內容了,經過本文的學習后,相信大家對ActivityManagerService廣播并行發送與串行發送怎么實現這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

通城县| 汾西县| 昌都县| 灌南县| 普格县| 内江市| 浪卡子县| 仁化县| 诸暨市| 嵊泗县| 门源| 榆树市| 安塞县| 钟祥市| 故城县| 鹿邑县| 犍为县| 南投市| 汪清县| 西平县| 巨野县| 南岸区| 南川市| 吴堡县| 茌平县| 济宁市| 洛隆县| 青川县| 新乡县| 乌拉特后旗| 六枝特区| 麻江县| 如皋市| 临猗县| 金秀| 宜章县| 双城市| 旬邑县| 无锡市| 清水县| 杭锦后旗|