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

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

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

引言

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

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

通過這篇文章你將學習到以下內容:

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

goAsync 是什么

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

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

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

BroadcastReceiver 的調度流程

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

動態(tài)處理

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

首先會調用 ActivityThread#ApplicationThread 類中 scheduleRegisteredReceiver 方法,最終會調用 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 實現(xiàn)。

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

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

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

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

靜態(tài)處理

首先會調用 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 實例,其中 rReceiverData 實例, ReceiverDataBroadcastReceiver.PendingResult 的子類。

在主線程消息隊列中接受 RECEIVER 消息,最后會調用 ActivityThread 中的 handleMessage 方法。

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

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

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

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

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

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

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

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

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

對進程的影響

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

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

如果滿足以下任一條件,則進程會被認為位于前臺。

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

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

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

在什么場景下使用 goAsync

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

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

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

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

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

如何使用 goAsync

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

public abstract class CloudMessagingReceiver extends BroadcastReceiver {
    public final void onReceive(final Context context, final Intent intent) {
        // 調用 goAsync() 返回新的 PendingResult,并將原 PendingResult 設置為 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 {
            /**
            * 處理耗時任務,如果任務在限定時間內處理完所有消息,主動調用 goAsync.finish() 方法結束當前的 Receiver
            **/
        } finally {
            goAsync.finish();
        }
    }
}

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

相關文章

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

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

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

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

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

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

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

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

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

    Android SharedPreference存儲文件三步走

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

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

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

    Android模擬美團客戶端進度提示框

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

    Android BSearchEdit 搜索結果選擇框的實例代碼

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

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

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

    Android ViewFlipper用法實例分析

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

最新評論

新晃| 静海县| 噶尔县| 叶城县| 昌宁县| 桃源县| 松江区| 大足县| 任丘市| 都昌县| 宝兴县| 阿图什市| 西乡县| 呼和浩特市| 延边| 威宁| 元朗区| 凤台县| 龙门县| 旬阳县| 临西县| 阿拉善右旗| 琼海市| 余姚市| 东阿县| 自治县| 桑日县| 博罗县| 信丰县| 镇沅| 江永县| 长宁区| 望谟县| 逊克县| 广平县| 涟源市| 镇巴县| 通河县| 郓城县| 县级市| 左贡县|