欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android廣播事件流程與廣播ANR原理深入刨析

 更新時間:2022年10月27日 16:00:43   作者:失落夏天  
這篇文章主要介紹了Android廣播事件流程與廣播ANR原理,ANR應(yīng)用程序未響應(yīng),當(dāng)主線程被阻塞時,就會彈出如下彈窗,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可任意參考一下

序言

本想寫廣播流程中ANR是如何觸發(fā)的,但是如果想講清楚ANR的流程,就不得不提整個廣播事件的流程,所以就把兩塊內(nèi)容合并在一起講了。

本文會講內(nèi)容如下:

1.動態(tài)注冊廣播的整個分發(fā)流程,從廣播發(fā)出,一直到廣播注冊者接收。

2.廣播類型ANR的判斷流程和原理。

PS:本文基于android13的源碼進(jìn)行講解。

一.基本流程和概念

動態(tài)廣播的流程其實是很簡單的,整套流程都在java層執(zhí)行,不涉及到native流程。

我們先來看一下最基本的流程圖:

首先是廣播注冊者,向AMS注冊廣播接收器,AMS交給BroadcastQueue來處理。也就是說BroadcastQueue中裝載所有的廣播接收器。然后廣播發(fā)出者向AMS發(fā)出廣播,這時候AMS仍然會交給BroadcastQueue來處理,在其中找到符合條件的接受者,然后向接受者發(fā)出廣播的通知。

然后我們再來了解一些最基本類的功能

ContextImp:Context的最終實現(xiàn)類,activity,application中的各種功能最終都是委托給其來處理的,廣播功能自然也不例外。

ActivityManagerService:負(fù)責(zé)所有應(yīng)用的Activity的流程,包括廣播六層呢。至于為什么廣播也由其來進(jìn)行處理,而不是搞一個BroadcastManagerService,估計是覺得廣播功能簡單不需要單獨(dú)設(shè)計Service吧。

BroadCastQueue:裝載所有的動態(tài)廣播接收器,靜態(tài)廣播也是由其來負(fù)責(zé)加載的,負(fù)責(zé)具體廣播的分發(fā)工作。一種有三種類型,前臺,后臺,離線。

BroadcastReceiver:廣播注冊者

二.無序廣播流程

首先我們了解下什么是無需廣播和有序廣播。簡單來說,有序廣播就是需要嚴(yán)格按照優(yōu)先級,依次的執(zhí)行接收動作,高優(yōu)先級的廣播接受者如果沒有處理完,低優(yōu)先級的廣播接受者是無法收到廣播的。無需廣播就是沒有順序,各個廣播接受者之間沒有相互依賴關(guān)系。

無需廣播,發(fā)送是sendBroadcast方法發(fā)送廣播通知。

有序廣播,需要使用sendOrderedBroadcast方法發(fā)送廣播通知。

無序廣播大體流程圖如下:

注冊廣播接收者流程

1.APP側(cè),無論activity還是application,還是service,最終都是交給Context的實現(xiàn)類ContentImpl進(jìn)行最終的處理的。所以注冊廣播的時候,最終調(diào)用的是ContentImpl的registerReceiver方法。

2.registerReceiver方法中通過binder,通知到AMS的registerReceiverWithFeature方法。

3.registerReceiverWithFeature方法中,首先做一些安全校驗。

4.除了action類型,還會有scheme的類型,這里就不具體介紹了。

5.最終都會注冊到IntentResolver類型對象mReceiverResolver上。

廣播通知流程

1.仍然交由ContextImpl中的broadcastIntentWithFeature方法來處理。

2.broadcastIntentWithFeature通知到AMS的broadcastIntentLocked方法中。

3.首先從mReceiverResolver中查詢,看是否存在接受者,如果存在,加入到registeredReceivers集合中。

registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);

4.如果mReceiverResolver不為空,并且是無序廣播,則根據(jù)intent找到所對應(yīng)的BroadcastQueue(根據(jù)前臺,后臺,離線的類型)。

if (!ordered && NR > 0) {
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, ...);
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            egisteredReceivers = null;
            NR = 0;
}

5.生成BroadcastRecord對象用來記錄這次的action所對應(yīng)的廣播事件,然后加入到BroadcastQueue的mParallelBroadcasts集合中。然后通過scheduleBroadcastsLocked方法切換到主線程執(zhí)行。這里要強(qiáng)調(diào)的是,無論是有序廣播還是無序廣播,都是通過這個方法來分發(fā)的。最終主線程會調(diào)用到processNextBroadcast方法。代碼在上面5,6行。

6.processNextBroadcast方法中邏輯很多,我們這里先只講無序廣播的流程。上一步中,我們把BroadcastRecord加入到了mParallelBroadcasts中,所以這里發(fā)送廣播的時候,直接遍歷mParallelBroadcasts集合,然后通知接受者接收。具體的流程可以看流程圖,這里就不細(xì)講了。

具體代碼如下:

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);//分發(fā)給接受者
            }
            addBroadcastToHistoryLocked(r);
        }

三.有序廣播流程

有序廣播流程和上面無序廣播是類似的,最終請求AMS的方法其實也是broadcastIntentWithFeature方法,兩者只是參數(shù)有區(qū)別而已。

我們?nèi)匀话凑兆詮V播接受者和發(fā)送廣播兩個流程來講,首先是注冊廣播。

注冊廣播接收者流程

注冊的流程和無序廣播是一樣的。

廣播通知流程

1.前面的流程和無序廣播是一樣的,唯一的區(qū)別是進(jìn)入到AMS的broadcastIntentLocked方法后,執(zhí)行邏略微有區(qū)別。有序廣播,調(diào)用的是BroadcastQueue中的

enqueueOrderedBroadcastLocked方法,把BroadcastRecord對象最終注冊到BroadcastDispatcher中的mOrderedBroadcasts集合中,然后在調(diào)用scheduleBroadcastsLocked方法切換到主線程,執(zhí)行processNextBroadcastLocked的主流程。

下面介紹的都是processNextBroadcastLocked方法,也是廣播事件分發(fā)的的核心流程代碼:

2.processNextBroadcastLocked中,首先仍然去無序隊列mParallelBroadcasts中查,這時候肯定是沒有值的,所以跳過這個步驟。

3.然后通過mDispatcher.getNextBroadcastLocked(now);方法去BroadcastDispatcher中查找,方法實現(xiàn)如下,因為剛才應(yīng)添加到了mOrderedBroadcasts集合中,所以這時候可以找到并返回BroadcastRecord對象。

4.BroadcastRecord.nextReceiver用來記錄一系列有序廣播執(zhí)行到了第幾個,0代表開始,如果為0,則記錄一些必要信息。另外,會更新掉record的時間receiverTime,這一點(diǎn)很重要,下面ANR流程中會有涉及到。

代碼如下:

int recIdx = r.nextReceiver++;
r.receiverTime = SystemClock.uptimeMillis();
        if (recIdx == 0) {
            r.dispatchTime = r.receiverTime;
            r.dispatchClockTime = System.currentTimeMillis();
            if (mLogLatencyMetrics) {
                FrameworkStatsLog.write(
                        FrameworkStatsLog.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 (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
                    + mQueueName + "] " + r);
        }

5.發(fā)送一個延時廣播,延時時間具體看是前臺廣播還是后臺廣播定義的。如果已經(jīng)發(fā)送過并且未結(jié)束,則跳過。setBroadcastTimeoutLocked中的具體邏輯,第四章ANR流程中會講到。

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

6.如果接受者是BroadcastFilter類型,則把廣播事件發(fā)送給接受者。

if (nextReceiver instanceof BroadcastFilter) {
    BroadcastFilter filter = (BroadcastFilter)nextReceiver;
    deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
 
}

至此,processNextBroadcastLocked的方法暫時介紹完成,即然用暫時,那就說明后面還會涉及到。

7.接收者是LoadedApk中ReceiverDispatcher的內(nèi)部類InnerReceiver,它是binder的服務(wù)端接收者,其performReceive方法收到通知后,會交給ReceiverDispatcher中的performReceive方法處理。

if (rd != null) {
   rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
}

所以,最終來處理廣播信息的方法是ReceiverDispatcher中的performReceive方法。其中部分代碼如下,getRunnable就是最終通知到我們注冊的廣播接受者的流程。如果getRunnable中的任務(wù)注冊不成功的話,會直接發(fā)送信號通知回AMS。什么情況下會注冊不成功呢?主線程Looper異常執(zhí)行完并退出時就會發(fā)生。

 if (intent == null || !mActivityThread.post(args.getRunnable())) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManager.getService();
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing sync broadcast to " + mReceiver);
                    args.sendFinished(mgr);
                }
            }
        }

我們在來看一下getRunnable中返回的任務(wù),我們?nèi)匀恢毁N核心代碼:

                    if (receiver == null || intent == null || mForgotten) {
                        if (mRegistered && ordered) {
                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                    "Finishing null broadcast to " + mReceiver);
                            sendFinished(mgr);
                        }
                        return;
                    }
                   ...
                    try {
                       ...
                        receiver.onReceive(mContext, intent);
                    } catch (Exception e) {
                        ...
                    }
                    if (receiver.getPendingResult() != null) {
                        finish();
                    }

我們可以看到,先執(zhí)行onReceive的回調(diào),這里就會最終通知到我們的BroadcastReceiver中的onReceive方法。

然后我們在看一下finish方法:這個其實核心就是發(fā)送廣播完成的信號給AMS:

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.PendingResult的sendFinished方法中,通過binder通知回AMS。

 am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                                mAbortBroadcast, mFlags);

8.AMS的finishReceiver中,主要功能是先通過binder,找到所對應(yīng)的BroadcastRecord,然后結(jié)束掉當(dāng)前的這個事件流程,如果后面還有事件,則繼續(xù)調(diào)用processNextBroadcastLocked方法,進(jìn)行下一輪的廣播事件分發(fā)。

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

四.廣播ANR流程

那么什么時候會導(dǎo)致廣播ANR呢?這一套題,我估計能考倒90%的安卓開發(fā)者,單純的無序廣播,廣播接受者中sleep幾分鐘,是不會產(chǎn)生廣播類型ANR的,ANR只存在于有序廣播并且廣播接受者未及時響應(yīng)。

其實了解完整個廣播流程,我們ANR流程就好講的多。我們只需關(guān)注整個流程中的幾個點(diǎn)就好了。

觸發(fā)廣播監(jiān)聽者流程

1.首先第三章的第五小節(jié)中,我們講到,通過setBroadcastTimeoutLocked方法設(shè)置一個延時的消息,消息的執(zhí)行時間=當(dāng)前時間+超時時間,此時伴隨著廣播通知到客戶端的流程。

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

也就是說,一旦達(dá)到超時時間,那么就會發(fā)送BROADCAST_TIMEOUT_MSG類型的事件,就一定會執(zhí)行broadcastTimeoutLocked方法。

2.broadcastTimeoutLocked方法中,會再一次進(jìn)行判斷,如果沒有到執(zhí)行時間,會重新觸發(fā)一次setBroadcastTimeoutLocked的流程。

上面3.8中講到,如果是有序廣播,廣播接收者收到消息后,會通過binder回調(diào)AMS通知事件處理完成,并重新進(jìn)入processNextBroadcastLocked流程進(jìn)行下一輪的分發(fā),這時候,會更新掉上面用到的receiverTime時間。

3.processNextBroadcastLocked分發(fā)廣播的時候,如果判斷進(jìn)入到了結(jié)束的環(huán)節(jié),會主動取消注冊BROADCAST_TIMEOUT_MSG類型的事件。

 if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
    ...
     cancelBroadcastTimeoutLocked();
}

一旦取消了超時類型消息的注冊,自然也不會走到ANR邏輯。

這里有一點(diǎn)繞,所以我舉個例子詳細(xì)解釋下,因為一開始我其實也被繞進(jìn)去了。

假設(shè)我們的超時時間為10S,有序廣播中有3個接收者,接收者1耗時5S,接收者2耗時6S,接收者3耗時4S,這時候,在接收者2處理中的時候,就會進(jìn)入到broadcastTimeoutLocked的流程。但是此時,由于接收者1執(zhí)行完其流程,所以更新了receiverTime時間,此時的超時時間變成5+10S,而當(dāng)前為第10S,所以并不會超時。第14S的時候接收者3也完成流程,通知回AMS,取消了超時類型消息的注冊,所以就不會再次走到broadcastTimeoutLocked的流程了。

所以,走到broadcastTimeoutLocked流程并不一定意味著就會發(fā)生ANR。我一開始就是被這個繞進(jìn)去了。

五.總結(jié)

所以整個廣播事件分發(fā)以及ANR觸發(fā)流程,我們可以用下圖來做一個總結(jié):圖片較大,建議另外開一個tab頁查看:

六.擴(kuò)展問題

1.有序和無序廣播是怎么區(qū)分的?

答:sendOrderedBroadcast和sendBroadcast兩個方法,其實最終broadcastIntentWithFeature方法中,就是一個參數(shù)不一樣,倒數(shù)第三個boolean serialized。sendOrderedBroadcast為true,sendBroadcast為false。

2.發(fā)送一個廣播,A進(jìn)程中接收,廣播接收者A中的onReceive方法中sleep100秒,是否一定會觸發(fā)ANR?如果是或者不是?原因是什么?如果我們把廣播改為有序廣播呢?

答:

只有有序廣播才會ANR,如果第一種情況如果是無序廣播,自然不會ANR。

第二個答案是一般情況下是會的,因為廣播接收者A中阻塞,導(dǎo)致AMS無法按時收到廣播完成的信號,從而會引起ANR。除非進(jìn)程A的主線程因為某種異常原因退出,不過這種情況下,onReceive方法自然也不會走到。

到此這篇關(guān)于Android廣播事件流程與廣播ANR原理深入刨析的文章就介紹到這了,更多相關(guān)Android廣播事件流程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論