最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Google大佬都用的廣播goAsync源碼分析

 更新時間:2023年01月13日 15:37:37   作者:程序員DHL  
這篇文章主要為大家介紹了Google大佬都用的廣播?goAsync源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

近期在分析問題過程中,需要反編譯 Google 的一些庫,在看源碼的時候,發(fā)現(xiàn)使用廣播的場景都會手動調(diào)用 goAsync() 方法。

goAsync() 是一個冷門但是非常有用的知識點(diǎn),很少有文章會去分析 goAsync() 方法,因此這個方法在實(shí)際項(xiàng)目中使用的人也非常的少,我之前對這個方法也只是有一點(diǎn)了解,帶著我的好奇心,研究了一下。

通過這篇文章你將學(xué)習(xí)到以下內(nèi)容:

  • goAsync() 是什么,它的作用是什么
  • BroadcastReceiver 如何處理靜態(tài)接受者和動態(tài)接受者
  • 為什么 goAsync 方法,可以保證廣播處于活躍狀態(tài)
  • 在什么場景下使用 goAsync()
  • 對進(jìn)程的影響

goAsync 是什么

根據(jù) BroadcastReceiver 源碼中的介紹,goAsync() 方法返回 PendingResult,可以在 BroadcastReceiver.onReceive() 中使用,如果調(diào)用了這個方法,當(dāng) onReceive() 方法執(zhí)行完返回時,并不會終止當(dāng)前的廣播,廣播依然處于活躍狀態(tài),直到調(diào)用 PendingResult.finish() 方法,才會結(jié)束掉當(dāng)前的廣播。

goAsync() 方法并不會影響廣播超時的策略,從調(diào)用 goAsync() 方法開始,一直到調(diào)用 finish() 方法結(jié)束,如果超過了源碼中設(shè)置的廣播超時時間(10s/60s),依然會產(chǎn)生 ANR。

為什么 goAsync() 方法,可以保證廣播處于活躍狀態(tài),我們需要先了解一下 BroadcastReceiver 調(diào)度流程,以 android-11.0.0_r3 源碼為例。

BroadcastReceiver 的調(diào)度流程

AMS 和應(yīng)用進(jìn)程之間的通信是通過 ApplicationThread 進(jìn)行的,而廣播處理的方式分為靜態(tài)處理和動態(tài)處理,在 ApplicationThread 中分別對這兩種方式做了處理。

動態(tài)處理

動態(tài)處理流程,如下所示:

首先會調(diào)用 ActivityThread#ApplicationThread 類中 scheduleRegisteredReceiver 方法,最終會調(diào)用 LoadedApk#ReceiverDispatcher 類中的 performReceive 方法。

frameworks/base/core/java/android/app/LoadedApk #ReceiverDispatcher . java

public void performReceive(Intent intent, ...) {
    final Args args = new Args(intent, resultCode, ...);
    if (intent == null || !mActivityThread.post(args.getRunnable())) {
        ....
    }
}

通過 mActivityThread. post () 發(fā)送一個 Runnable, 我看一下 Args 中的 Runnable 實(shí)現(xiàn)。

frameworks/base/core/java/android/app/LoadedApk #ReceiverDispatcher #Args . java

public void Runnable getRunnable() {
    return () -> {
        // 這個是我們注冊的 BroadcastReceiver
        final BroadcastReceiver receiver = mReceiver;
        try {
            ......
            // 為注冊的廣播接受者設(shè)置 PendingResult
            receiver.setPendingResult(this);
            // 執(zhí)行 BroadcastReceiver#onReceive 方法
            receiver.onReceive(mContext, intent);
        } catch (Exception e) {
        }
        // 判斷 PendingResult 是否為空,如果為空,就不會結(jié)束掉當(dāng)前注冊的 Receiver
        // 應(yīng)用層可以調(diào)用 BroadcastReceiver.goAsync,將 PendingResult 設(shè)置為null,從而打斷廣播后續(xù)處理流程
        if (receiver.getPendingResult() != null) {
            finish();
        }
    };
}

Runnable 方法實(shí)現(xiàn)分為兩個部分:

  • 執(zhí)行 BroadcastReceiver.onReceive() 方法之前會設(shè)置 PendingResult
  • BroadcastReceiver.onReceive() 方法執(zhí)行完后,檢查 PendingResult 是否為空,如果為空,就不會結(jié)束掉當(dāng)前注冊的 BroadcastReceiver

靜態(tài)處理

首先會調(diào)用 ActivityThread#ApplicationThread 類中 scheduleReceiver 方法。

frameworks/base/core/java/android/app/ActivityThread #ApplicationThread . java

public final void scheduleReceiver(Intent intent, ...) {
    sendMessage(H.RECEIVER, r);
}

通過 sendMessage(H.RECEIVER, r) 方法往主線程拋一個 RECEIVER 消息,發(fā)送 RECEIVER 消息的同時會攜帶 ReceiverData 實(shí)例,其中 rReceiverData 實(shí)例, ReceiverDataBroadcastReceiver.PendingResult 的子類。

在主線程消息隊(duì)列中接受 RECEIVER 消息,最后會調(diào)用 ActivityThread 中的 handleMessage 方法。

frameworks/base/core/java/android/app/ActivityThread. java

private void handleReceiver(ReceiverData data) {
    BroadcastReceiver receiver;
    try {
        // 通過反射構(gòu)造一個 BroadcastReceiver 實(shí)例
        receiver = packageInfo.getAppFactory()
                .instantiateReceiver(cl, data.info.name, data.intent);
    } catch (Exception e) {
    }
    ......
    try {
        // 為注冊的廣播接受者設(shè)置 PendingResult
        // data 是 ReceiverData 實(shí)例, ReceiverData 是 BroadcastReceiver.PendingResult 的子類
        receiver.setPendingResult(data);
        // 執(zhí)行 BroadcastReceiver#onReceive 方法
        receiver.onReceive(context.getReceiverRestrictedContext(),
                data.intent);
    } catch (Exception e) {
       ......
    } 
    // 判斷 PendingResult 是否為空,如果為空,就不會結(jié)束掉當(dāng)前注冊的 Receiver
    // 應(yīng)用層可以調(diào)用 BroadcastReceiver.goAsync,將 PendingResult 設(shè)置為 null,從而打斷廣播后續(xù)處理流程
    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}

handleMessage 方法實(shí)現(xiàn)分為兩個部分:

  • 通過反射構(gòu)造一個 BroadcastReceiver 實(shí)例
  • 執(zhí)行 BroadcastReceiver.onReceive() 方法之前會設(shè)置 PendingResult
  • BroadcastReceiver.onReceive() 方法執(zhí)行完后,檢查 PendingResult 是否為空,如果為空,就不會結(jié)束掉當(dāng)前注冊的 BroadcastReceiver

靜態(tài)處理和動態(tài)處理,最終的處理流程都是一樣的,唯一的區(qū)別靜態(tài)處理是通過反射構(gòu)造一個 BroadcastReceiver 實(shí)例。

為什么 goAsync 方法,可以保證廣播處于活躍狀態(tài)

通過上面的源碼分析,我們可以知道只需要將 PendingResult 設(shè)置為 null,不會馬上結(jié)束掉當(dāng)前的廣播,相當(dāng)于 "延長了廣播的生命周期",因此 Google 提供了 goAsync() 方法給開發(fā)者調(diào)用,當(dāng)調(diào)用 goAsync() 時,不會結(jié)束掉當(dāng)前的廣播,讓廣播依然處于活躍狀態(tài)。goAsync() 方法的實(shí)現(xiàn)很簡單。

public final PendingResult goAsync() {
    PendingResult res = mPendingResult;
    mPendingResult = null;
    return res;
}

goAsync() 方法主要將 PendingResult 設(shè)置為 null,當(dāng) BroadcastReceiver.onReceive() 方法執(zhí)行結(jié)束,會檢查 PendingResult 是否為 null,如果為 null 不會結(jié)束掉當(dāng)前的 BroadcastReceiver,需要開發(fā)者在合適的時機(jī)主動調(diào)用 PendingResult.finish() 方法,手動結(jié)束掉當(dāng)前 BroadcastReceiver,否則會觸發(fā)廣播的超時機(jī)制(10s/60s) 發(fā)生 ANR。

對進(jìn)程的影響

BroadcastReceiver 的狀態(tài)會影響其所在進(jìn)程的狀態(tài),而進(jìn)程的狀態(tài)又會影響它被系統(tǒng)回收的可能性。因?yàn)榍芭_進(jìn)程和后臺進(jìn)程,系統(tǒng)對它們的影響是不同的。

如何區(qū)分前臺進(jìn)程

如果滿足以下任一條件,則進(jìn)程會被認(rèn)為位于前臺。

  • 它正在用戶的互動屏幕上運(yùn)行一個 Activity(其 onResume() 方法已被調(diào)用)。
  • 它有一個 BroadcastReceiver 目前正在運(yùn)行(其 BroadcastReceiver.onReceive() 方法正在執(zhí)行)
  • 它有一個 Service 目前正在執(zhí)行其某個回調(diào)(Service.onCreate()Service.onStart()Service.onDestroy())中的代碼。

所以你不應(yīng)該在 onReceive() 中啟動一個長時間運(yùn)行的子線程,當(dāng) onReceive() 方法執(zhí)行完返回時,BroadcastReceiver 就不再活躍,系統(tǒng)會將其進(jìn)程視為低優(yōu)先級進(jìn)程,系統(tǒng)會根據(jù)內(nèi)存情況來回收,在此過程中,也會終止進(jìn)程中運(yùn)行的派生線程。

所以如果你要在子線程中運(yùn)行一個長時間的任務(wù),我們可以使用 goAsync() 方法,它會中斷廣播后續(xù)處理流程,讓 BroadcastReceiver 處于活躍狀態(tài),即使 onReceive() 方法執(zhí)行完,也不會結(jié)束掉當(dāng)前 BroadcastReceiver,除非主動調(diào)用 PendingResult.finish() 方法。

在什么場景下使用 goAsync

BroadcastReceiver. onReceive () 方法運(yùn)行在主線程中,如果我們在主線程做耗時任務(wù)就會出現(xiàn) ANR。

PS:關(guān)于廣播 ANR 發(fā)生的場景、解決方案、源碼分析,將會在后面穩(wěn)定性系列文章中分析

如果有耗時任務(wù),大部分同學(xué)的做法是,直接在 onReceive () 方法中起子線程處理耗時任務(wù),當(dāng) onReceive () 方法返回時,BroadcastReceiver 不會在處于活躍狀態(tài),那么廣播所在的進(jìn)程也會受到影響,如果當(dāng)前 BroadcastReceiver 所在的進(jìn)程被系統(tǒng)回收了,那么子線程中的任務(wù)也會受到影響。

一般的處理方式會通過 IntentService、JobService 方式,保證任務(wù)能夠正常的執(zhí)行完,但是使用 Service 的方式會帶來很多的問題,因?yàn)?Service 是通過 AMS 進(jìn)行跨進(jìn)程調(diào)度,AMS 調(diào)度也會有超時機(jī)制,如果因?yàn)橄到y(tǒng)原因,或者未知原因,導(dǎo)致 AMS 調(diào)度延遲了,ANR 的概率會增大,而且代碼的復(fù)雜度也變高了。

Google 也注意到這一點(diǎn),所以在 BroadcastReceiver 調(diào)度流程中留出來一個入口。增加了一個靜態(tài)內(nèi)部類 PendingResult,并且提供了 goAsync () 方法給開發(fā)者調(diào)用,如果你需要運(yùn)行一個長時間的任務(wù),在切換到子線程之前,需要調(diào)用 goAsync () 方法,讓廣播處于活躍狀態(tài),在系統(tǒng)限制的時間內(nèi),處理完任務(wù)之后,主動調(diào)用 PendingResult. finish () 方法,結(jié)束掉當(dāng)前的廣播。

如何使用 goAsync

這里我以 Google play services cloud messaging 中的源碼為例。

public abstract class CloudMessagingReceiver extends BroadcastReceiver {
    public final void onReceive(final Context context, final Intent intent) {
        // 調(diào)用 goAsync() 返回新的 PendingResult,并將原 PendingResult 設(shè)置為 null
        final BroadcastReceiver.PendingResult goAsync = goAsync();
        // 開啟線程處理接受的消息,并將 goAsync 傳遞到子線程
        getBroadcastExecutor().execute(new Runnable() {
            @Override 
            public final void run() {
                parseIntent(intent, , goAsync);
            }
        });
    }
    public final void parseIntent(Intent intent, BroadcastReceiver.PendingResult goAsync) {
        try {
            /**
            * 處理耗時任務(wù),如果任務(wù)在限定時間內(nèi)處理完所有消息,主動調(diào)用 goAsync.finish() 方法結(jié)束當(dāng)前的 Receiver
            **/
        } finally {
            goAsync.finish();
        }
    }
}

以上就是Google大佬都用的廣播 goAsync源碼分析的詳細(xì)內(nèi)容,更多關(guān)于Google廣播 goAsync的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android 添加TextView刪除線(代碼簡單)

    Android 添加TextView刪除線(代碼簡單)

    最近接了個項(xiàng)目,其中有項(xiàng)目需求是這樣的,有這么個需求,就是一個產(chǎn)品下有兩個價格,一個是市場價,一個是銷售價,這時要把市場價添加個刪除線;怎么實(shí)現(xiàn)呢?下面小編給大家分享一段簡單的代碼實(shí)現(xiàn)Android 添加TextView刪除線
    2016-02-02
  • Android實(shí)現(xiàn)移動小球和CircularReveal頁面切換動畫實(shí)例代碼

    Android實(shí)現(xiàn)移動小球和CircularReveal頁面切換動畫實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于利用Android如何實(shí)現(xiàn)移動的小球和CircularReveal頁面切換動畫的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-09-09
  • Android實(shí)現(xiàn)代碼畫虛線邊框背景效果

    Android實(shí)現(xiàn)代碼畫虛線邊框背景效果

    可能之前遇到這樣的需求大家都會想到用圖片背景來解決,下面這篇文章將給大家介紹Android如何利用代碼畫虛線邊框背景的效果,有需要的朋友們可以參考借鑒,下面來跟著小編一起學(xué)習(xí)學(xué)習(xí)吧。
    2016-12-12
  • Android開發(fā)筆記 Handler使用總結(jié)

    Android開發(fā)筆記 Handler使用總結(jié)

    當(dāng)應(yīng)用程序啟動時,Android首先會開啟一個主線程(也就是UI線程),主線程為管理界面中的UI控件,進(jìn)行事件分發(fā)
    2012-11-11
  • Android SharedPreference存儲文件三步走

    Android SharedPreference存儲文件三步走

    SharedPreferences是安卓平臺上一個輕量級的存儲類,用來保存應(yīng)用的一些常用配置,比如Activity狀態(tài),Activity暫停時,將此activity的狀態(tài)保存到SharedPereferences中;當(dāng)Activity重載,系統(tǒng)回調(diào)方法onSaveInstanceState時,再從SharedPreferences中將值取出
    2023-01-01
  • android實(shí)現(xiàn)接通和掛斷電話

    android實(shí)現(xiàn)接通和掛斷電話

    這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)接通和掛斷電話功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Android模擬美團(tuán)客戶端進(jìn)度提示框

    Android模擬美團(tuán)客戶端進(jìn)度提示框

    這篇文章主要為大家詳細(xì)介紹了Android模擬美團(tuán)客戶端進(jìn)度提示框的實(shí)現(xiàn)過程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2015-09-09
  • Android BSearchEdit 搜索結(jié)果選擇框的實(shí)例代碼

    Android BSearchEdit 搜索結(jié)果選擇框的實(shí)例代碼

    EditText搜索結(jié)果下拉框、自動or回調(diào)模式、可diy、使用超簡便。這篇文章主要介紹了Android BSearchEdit 搜索結(jié)果選擇框的實(shí)例代碼,需要的朋友可以參考下
    2019-10-10
  • android使用webwiew載入頁面使用示例(Hybrid App開發(fā))

    android使用webwiew載入頁面使用示例(Hybrid App開發(fā))

    Hybrid App 融合 Web App 的原理就是嵌入一個WebView組件,可以在這個組件中載入頁面,相當(dāng)于內(nèi)嵌的瀏覽器,下面是使用示例
    2014-03-03
  • Android ViewFlipper用法實(shí)例分析

    Android ViewFlipper用法實(shí)例分析

    這篇文章主要介紹了Android ViewFlipper用法,結(jié)合實(shí)例形式分析了ViewFlipper圖片操作的相關(guān)技巧,需要的朋友可以參考下
    2016-01-01

最新評論

呼玛县| 睢宁县| 乌兰浩特市| 饶平县| 芜湖县| 合水县| 和硕县| 博乐市| 大新县| 靖安县| 昌邑市| 黄梅县| 郎溪县| 商丘市| 师宗县| 绿春县| 曲周县| 广饶县| 闽侯县| 深泽县| 绥棱县| 小金县| 林西县| 玉林市| 综艺| 张家界市| 固镇县| 邛崃市| 孟州市| 永济市| 年辖:市辖区| 思南县| 读书| 齐齐哈尔市| 荔波县| 治多县| 德安县| 沂水县| 富平县| 阳谷县| 永登县|