framework 廣播基礎(chǔ)知識(shí)和常見問題

基礎(chǔ)知識(shí)

framework層廣播相關(guān)的邏輯主要在AMS.java和BroadcastQueue.java中,代表一個(gè)廣播的是BroadcastRecord。

分類

  1. 注冊(cè)方式:
    • 靜態(tài)注冊(cè): android manifest文件注冊(cè),常駐廣播,分發(fā)慢。
    • 動(dòng)態(tài)注冊(cè):代碼中注冊(cè),非常駐,分發(fā)快。
  2. 接收者:
    • 顯示廣播:指定接收方的class類型
    • 隱式廣播:只指定action,uri等, android 8.0開始限制了隱式廣播的接收
  3. 發(fā)送方式:
    靜態(tài)注冊(cè)的廣播,一律按照串行廣播處理,因?yàn)榭赡軙?huì)設(shè)計(jì)到拉起多個(gè)進(jìn)程等,串行廣播才有超時(shí)處理。
    • 有序廣播:串行分發(fā),效率低,可以通過abortBroadcast截?cái)?在入隊(duì)時(shí)會(huì)根據(jù)優(yōu)先級(jí)對(duì)receiver進(jìn)行排序。
    • 無序廣播:默認(rèn)無序;并行分發(fā);不可以被攔截、終止、修改;無優(yōu)先級(jí)問題;傳遞數(shù)據(jù)用intent.putExtra
  4. 處理類型:
    AMS中前臺(tái)廣播和后臺(tái)廣播分別有一個(gè)廣播隊(duì)列,互不干擾。
    • 前臺(tái)廣播:發(fā)送時(shí)添加Intent.FLAG_RECEIVER_FOREGROUND,超時(shí)時(shí)間10s
    • 后臺(tái)廣播:默認(rèn)后臺(tái),超時(shí)時(shí)間60s

常見命令

  1. dumpsys activity broadcasts
    • Registered Receivers: 列出所有已注冊(cè)的receivers列表
    • Receiver Resolver Table: receiver的解析表
    • Historical broadcasts [foreground]: 前臺(tái)廣播歷史記錄
    • Historical broadcasts summary [foreground]: 前臺(tái)廣播歷史記錄簡(jiǎn)要
    • Historical broadcasts [background]:后臺(tái)廣播歷史記錄
    • Historical broadcasts summary [background]: 后臺(tái)廣播歷史記錄簡(jiǎn)要
 // 已注冊(cè)的receiver
  * ReceiverList{7657857 3271 com.android.systemui/1000/u0 remote:5d6fdd6}
    app=3271:com.android.systemui/1000 pid=3271 uid=1000 user=0 // 進(jìn)程相關(guān)信息,pid,uid,user等
    Filter #0: BroadcastFilter{b28ef44} // 列出當(dāng)前receiver的所有BroadcastFilter
      Action: "android.intent.action.CLOSE_SYSTEM_DIALOGS"
// receiver的查詢表
android.intent.action.CLOSE_SYSTEM_DIALOGS:
  BroadcastFilter{cd0b64d 1000/u0 ReceiverList{5cd41e4 2477 system/1000/u0 local:7b2f177}}
  BroadcastFilter{766ac91 1000/u0 ReceiverList{b2bd6b8 2477 system/1000/u0 local:dd6451b}}
  BroadcastFilter{707a39e 1000/u-1 ReceiverList{6db3dd9 2477 system/1000/u-1 local:28e4020}}
  BroadcastFilter{921cf0a 1000/u0 ReceiverList{a168375 3271 com.android.systemui/1000/u0 remote:792eaac}}
  BroadcastFilter{eb0f625 1000/u-1 ReceiverList{f9ba1c 3271 com.android.systemui/1000/u-1 remote:311c78f}}
  BroadcastFilter{b28ef44 1000/u0 ReceiverList{**7657857** 3271 com.android.systemui/1000/u0 remote:5d6fdd6}}
  BroadcastFilter{3dc5af6 1000/u0 ReceiverList{9ad5b91 2477 system/1000/u0 local:ab3e1b8}}
  BroadcastFilter{84a1c2 1000/u0 ReceiverList{27a8e0d 5062 com.miui.securitycenter.remote/1000/u0 remote:49ca0a4}}
  BroadcastFilter{6151148 1000/u0 ReceiverList{6ac8feb 5062 com.miui.securitycenter.remote/1000/u0 remote:17c273a}}
  BroadcastFilter{1525a45 1000/u0 ReceiverList{caf9abc 5062 com.miui.securitycenter.remote/1000/u0 remote:aa646af}}
  Historical Broadcast foreground #1: // 前臺(tái)廣播隊(duì)列第一條廣播
    BroadcastRecord{8888912 u0 com.xiaomi.mi_connect_service.mi_mover_endpoint_found} to user 0 // user 0
    Intent { act=com.xiaomi.mi_connect_service.mi_mover_endpoint_found flg=0x11000030 (has extras) } // flag有:FLAG_RECEIVER_FOREGROUND | FLAG_RECEIVER_INCLUDE_BACKGROUND | |FLAG_INCLUDE_STOPPED_PACKAGES | FLAG_EXCLUDE_STOPPED_PACKAGES
      extras: Bundle[{STRIPPED=1}]
    caller=com.xiaomi.mi_connect_service 9219:com.xiaomi.mi_connect_service/1000 pid=9219 uid=1000 // 發(fā)送廣播app的信息
    requiredPermissions=[com.xiaomi.mi_connect_service.permission.RECEIVE_ENDPOINT]  appOp=-1 // 廣播要求receiver的權(quán)限
    enqueueClockTime=2021-09-11 16:04:57.623 dispatchClockTime=2021-09-11 16:04:57.623 // 入隊(duì)時(shí)間和分發(fā)時(shí)間
    dispatchTime=-1m2s207ms (0 since enq) finishTime=-1m2s164ms (+43ms since disp)  // 結(jié)束時(shí)間 & 分發(fā)用時(shí)
    resultTo=null resultCode=0 resultData=null // 一般resultCode為0且resultTo為null且無” ordered= “表示靜態(tài)注冊(cè)的無序廣播
    nextReceiver=1 receiver=null
    Deliver +42ms #0: (manifest) //派發(fā)給靜態(tài)注冊(cè)的receiver
      priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=false //優(yōu)先級(jí)默認(rèn)為0,
      ActivityInfo: // receiver的信息
        name=com.miui.huanji.ble.MiConnectBleReceiver
        packageName=com.miui.huanji
        enabled=true exported=true directBootAware=false
        resizeMode=RESIZE_MODE_RESIZEABLE
Historical Broadcast background #29: // 后臺(tái)廣播隊(duì)列第29條
BroadcastRecord{e0ebea0 u-1 android.intent.action.PHONE_STATE{color}} to user -1 // -1代表user_all
Intent { act=android.intent.action.PHONE_STATE flg=0x1000010 (has extras) } // intent信息
extras: Bundle[\{incoming_number=18381098208, state=IDLE}]
caller=android 1309:system/1000 pid=1309 uid=1000 // 發(fā)送廣播app的信息
requiredPermissions=[android.permission.READ_PHONE_STATE, android.permission.READ_CALL_LOG] appOp=-1  // 廣播要求receiver的權(quán)限
enqueueClockTime=2021-06-23 11:41:05.687 dispatchClockTime=2021-06-23 11:41:05.687  // 入隊(duì)時(shí)間和分發(fā)時(shí)間
dispatchTime=-1m39s763ms (0 since enq) finishTime=-1m39s755ms (+8ms since disp)  // 結(jié)束時(shí)間 & 分發(fā)用時(shí)
// 分發(fā)每一個(gè)receiver的分發(fā)信息以及狀態(tài)
// Deliver代表當(dāng)前receiver已分發(fā)到
Deliver 0 #0: BroadcastFilter{12c9c23 1000/u-1 ReceiverList{5b06152 1309 system/1000/u-1 local:4a651dd}} 
Deliver 0 #1: BroadcastFilter{672acd7 1000/u0 ReceiverList{5be3456 1780 com.android.systemui/1000/u0 remote:b33e71}}
Deliver 0 #2: BroadcastFilter{f932daf 1000/u-1 ReceiverList{1ef518e 1780 com.android.systemui/1000/u-1 remote:35e2d89}}
Deliver 0 #3: BroadcastFilter{d0d467b 1001/u0 ReceiverList{b4a5e0a 1996 com.android.phone/1001/u0 remote:65bee75}}
Deliver 0 #4: BroadcastFilter{4081988 1002/u0 ReceiverList{661912b 2315 com.xiaomi.bluetooth/1002/u0 remote:34b657a}}
Deliver 0 #5: BroadcastFilter{b6bda66 1000/u0 ReceiverList{73672c1 5261 com.miui.powerkeeper/1000/u0 remote:bc317a8}}
// skip表示由于權(quán)限等原因,跳過分發(fā)到該app
Skipped 0 #6: BroadcastFilter{ebabe72 10243/u0 ReceiverList{5596f7d 21465 com.smile.gifmaker/10243/u0 remote:96d61d4}} 
Skipped 0 #7: BroadcastFilter{f7d90ab 10243/u0 ReceiverList{261b2fa 21465 com.smile.gifmaker/10243/u0 remote:ef8c125}}
Skipped 0 #8: BroadcastFilter{1f05292 10250/u0 ReceiverList{396401d 12339 com.tencent.mobileqq/10250/u0 remote:e8e90f4}}
Skipped 0 #9: BroadcastFilter{d98737f 10250/u0 ReceiverList{469cc9e 12339 com.tencent.mobileqq/10250/u0 remote:b386ad9}}
Skipped 0 #10: BroadcastFilter{4e1588 10250/u0 ReceiverList{851fd2b 29070 com.tencent.mobileqq:tool/10250/u0 remote:64a017a}}
  1. dumpsys activity broadcast-stats :廣播的統(tǒng)計(jì)
android.intent.action.DROPBOX_ENTRY_ADDED:
  Number received: 0, skipped: 302  // 分發(fā)0次,跳過302次
  Total dispatch time: +24s731ms, max: +13s538ms // 總分發(fā)用時(shí),和最大分發(fā)用時(shí)
  Package android: 151 times  
  Bg Check Violation com.google.android.gms: 302 times

常見的一些Intent中的flag

  1. FLAG_RECEIVER_REGISTERED_ONLY
    表示當(dāng)前廣播只允許動(dòng)態(tài)注冊(cè)的receiver接收,避免避免一些系統(tǒng)廣播(如TIME_TICK等)拉起靜態(tài)注冊(cè)的app進(jìn)程。
  2. FLAG_RECEIVER_REPLACE_PENDING
    如果廣播隊(duì)列中存在沒有被處理的相同廣播,則直接替換掉之前的廣播,無序再入隊(duì)分發(fā)
  3. FLAG_RECEIVER_FOREGROUND
    發(fā)送廣播時(shí)添加這個(gè)flag,廣播將會(huì)被添加到前臺(tái)廣播隊(duì)列中。

AMS中的一些屬性

// 已注冊(cè)的動(dòng)態(tài)廣播的receiver,receiver IBinder為key, ReceiverList為value;ReceiverList繼承自ArrayList<BroadcastFilter>
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();  

// 記錄著已注冊(cè)廣播的receiver的resolver,廣播發(fā)送時(shí)可根據(jù)intent和type等查詢得到對(duì)應(yīng)的動(dòng)態(tài)廣播接收者
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
            = new IntentResolver<BroadcastFilter, BroadcastFilter>();

BroadcastRecord的一些屬性

int state; //當(dāng)前廣播狀態(tài),初始是idle
final int[] delivery; // 每一個(gè)receiver的處理狀態(tài)
final List receivers; // 廣播的接收者列表,包括靜態(tài)注冊(cè)的ResolveInfo和動(dòng)態(tài)注冊(cè)的BroadcastFilter
int nextReceiver; // 下一個(gè)要處理的廣播接收者的索引,對(duì)應(yīng)于receivers的索引
IIntentReceiver resultTo; // 有序廣播指定的最后一個(gè)接收者
long enqueueClockTime;  // 廣播的入隊(duì)時(shí)間
long dispatchTime; // 分發(fā)時(shí)間
long receiverTime; //開始派發(fā)廣播到app端的時(shí)間,用于計(jì)算anr超時(shí)時(shí)間
long finishTime;  //結(jié)束廣播時(shí)間
IBinder receiver; // 當(dāng)前正在處理的receiver(ReceiverDispatcher.InnerReceiver),有序廣播和靜態(tài)注冊(cè)的才會(huì)賦值

ResolveInfo的一些屬性

public int priority; // 優(yōu)先級(jí)
public int preferredOrder; //偏好優(yōu)先級(jí)
public int match; // 系統(tǒng)評(píng)估活動(dòng)與IntentFilter的匹配情況。
public int specificIndex = -1; //僅當(dāng)由 queryIntentActivityOptions(ComponentName, Intent[], Intent, int)返回時(shí)設(shè)置,這告訴你這個(gè)結(jié)果來自哪個(gè)給定的特定意圖。
public boolean isDefault; //此過濾器指定了Intent.CATEGORY_DEFAULT,這意味著它將被視為用戶可以對(duì)此數(shù)據(jù)執(zhí)行的默認(rèn)操作。

廣播的狀態(tài)

static final int IDLE = 0;  // 廣播的初始狀態(tài),派發(fā)時(shí)只處理這種狀態(tài)的廣播
static final int APP_RECEIVE = 1; // 表示廣播正在派發(fā)到app進(jìn)程,靜態(tài)receiver
static final int CALL_IN_RECEIVE = 2; // 表示廣播正在派發(fā)到app進(jìn)程,有序廣播,動(dòng)態(tài)receiver
static final int CALL_DONE_RECEIVE = 3; // 廣播已經(jīng)派發(fā)到app進(jìn)程,有序廣播,動(dòng)態(tài)receiver
static final int WAITING_SERVICES = 4; // 等待后臺(tái)service啟動(dòng)

廣播接收者的處理狀態(tài)

    static final int DELIVERY_PENDING = 0; // 默認(rèn)狀態(tài)
    static final int DELIVERY_DELIVERED = 1; // 表示已經(jīng)派發(fā)到app進(jìn)程
    static final int DELIVERY_SKIPPED = 2; // 表示由于某種原因,未派發(fā)到app進(jìn)程
    static final int DELIVERY_TIMEOUT = 3; // 表示派發(fā)到app端,超時(shí)未結(jié)束派發(fā)

廣播不被處理的情況

實(shí)際上廣播不被處理的情況有很多種,這里只是列出常遇到的一些情況。

接收端未聲明廣播指定權(quán)限

W/BroadcastQueue: Permission Denial: receiving Intent { act=android.intent.action.ORDER_BROADCAST flg=0x10 } to com.example.myapplication/com.example.receive.MyReceiver requires com.android.permission.ORDER_PERMISSION due to sender com.example.testapplication (uid 10255)

在BroadcastQueue的deliverToRegisteredReceiverLocked(動(dòng)態(tài)注冊(cè))或processNextBroadcastLocked(靜態(tài)注冊(cè))方法中,會(huì)去校驗(yàn)接收端是否都具備廣播要求的權(quán)限,如果不具備,會(huì)跳過此次廣播的分發(fā)到app端。

      if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
            for (int i = 0; i < r.requiredPermissions.length; i++) {
                String requiredPermission = r.requiredPermissions[i];
                int perm = mService.checkComponentPermission(requiredPermission,
                        filter.receiverList.pid, filter.receiverList.uid, -1, true);
                if (perm != PackageManager.PERMISSION_GRANTED) {
                    Slog.w(TAG, "Permission Denial: receiving " + r.intent.toString() + " to " + filter.receiverList.app + " (pid=" + filter.receiverList.pid
                            + ", uid=" + filter.receiverList.uid + ")" + " requires " + requiredPermission + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
                    skip = true;
                    break;
                }

android 8.0對(duì)隱式廣播的限制

W BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_RESTARTED dat=package:com.android.soundrecorder flg=0x10 (has extras) } to com.xiaomi.misubscreenui/.receiver.LightDeviceStatusReceiver

限制如下兩種情況下的廣播分發(fā)
  • Intent中添加了FLAG_RECEIVER_EXCLUDE_BACKGROUND
  • Intent中無FLAG_RECEIVER_INCLUDE_BACKGROUND flag的隱式廣播
FLAG_RECEIVER_INCLUDE_BACKGROUND 添加的場(chǎng)景

也就是如下這些隱式廣播處于后臺(tái)清單文件注冊(cè)的也能接收到

  • ACTION_BOOT_COMPLETED

@BroadcastBehavior(includeBackground = true)
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";

  • allow-implicit-broadcast 列表內(nèi)的intent會(huì)在broadcastIntentLocked中添加
        if (action != null) {
            if (getBackgroundLaunchBroadcasts().contains(action)) {
                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
            }

20    <!-- Broadcast actions that are currently exempted from O+ background
21         delivery restrictions. -->
22    <allow-implicit-broadcast action="android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED" />
23    <allow-implicit-broadcast action="android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED" />
24    <allow-implicit-broadcast action="android.intent.action.DATA_SMS_RECEIVED" />
25    <allow-implicit-broadcast action="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
26    <allow-implicit-broadcast action="android.intent.action.PACKAGE_CHANGED" />
27    <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
28    <allow-implicit-broadcast action="android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION" />
29    <allow-implicit-broadcast action="android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION" />
30    <allow-implicit-broadcast action="android.provider.Telephony.SECRET_CODE" />
31    <allow-implicit-broadcast action="android.provider.Telephony.SMS_CB_RECEIVED" />
32    <allow-implicit-broadcast action="android.provider.Telephony.SMS_DELIVER" />
33    <allow-implicit-broadcast action="android.provider.Telephony.SMS_RECEIVED" />
34    <allow-implicit-broadcast action="android.provider.Telephony.SMS_REJECTED" />
35    <allow-implicit-broadcast action="android.provider.Telephony.WAP_PUSH_DELIVER" />
36    <allow-implicit-broadcast action="android.provider.Telephony.WAP_PUSH_RECEIVED" />
37    <allow-implicit-broadcast action="android.telephony.action.CARRIER_CONFIG_CHANGED" />
38    <allow-implicit-broadcast action="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
39    <allow-implicit-broadcast action="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
40    <allow-implicit-broadcast action="android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
41    <allow-implicit-broadcast action="android.telephony.action.SECRET_CODE" />
42    <allow-implicit-broadcast action="android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
43    <allow-implicit-broadcast action="android.telephony.action.SIM_CARD_STATE_CHANGED" />
44    <allow-implicit-broadcast action="android.telephony.action.SIM_SLOT_STATUS_CHANGED" />

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj, boolean parallelOnce) {
    .....
    boolean skip = false;
    if (!skip) {
        // 檢查當(dāng)前app的啟動(dòng)權(quán)限
        final int allowed = mService.getAppStartModeLocked(
                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
                info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false, r.callerPackage);
        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
            if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                skip = true;
            // 1. FLAG_RECEIVER_EXCLUDE_BACKGROUND , 2. 隱式廣播的限制
            } 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());
                Slog.w(TAG, "Background execution not allowed: receiving " + r.intent + " to "
                        + component.flattenToShortString());
                skip = true;
            }
        }
    }
 }

進(jìn)程頻繁crash

W BroadcastQueue: Unable to launch app com.bsp.catchlog/1000 for broadcast Intent { act=android.provider.Telephony.SECRET_CODE dat=android_secret_code://284 flg=0x1400010 }: process is bad

在AppErrors中處理當(dāng)前crash時(shí)會(huì)去判斷是否標(biāo)記當(dāng)前進(jìn)程為bad進(jìn)程。

    boolean handleAppCrashLocked(ProcessRecord app, String reason,
            String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
      .......
        if (crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) {
            Slog.w(TAG, "Process " + app.info.processName
                    + " has crashed too many times: killing!");
            EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                    app.userId, app.info.processName, app.uid);
            mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
            if (!app.isPersistent()) {
                EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
                        app.info.processName);
                if (!app.isolated) {
              
                    mBadProcesses.put(app.info.processName, app.uid,
                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
                    mProcessCrashTimes.remove(app.info.processName, app.uid);
                }
                app.bad = true;
                app.removed = true;
            .......
    }

而在啟動(dòng)進(jìn)程時(shí),ProcessList的startProcessLocked中會(huì)去判斷如果時(shí)后臺(tái)啟動(dòng)且進(jìn)程為bad,則返回null,終止啟動(dòng)進(jìn)程
        ProcessRecord app;
        if (!isolated) {
            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
            if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
                if (mService.mAppErrors.isBadProcessLocked(info)) {
                    return null;
                }
            }

BroadcastQueue中processNextBroadcastLocked中進(jìn)程不存在去創(chuàng)建進(jìn)程時(shí)如果失敗,就會(huì)打印上面那行l(wèi)og

        if ((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,
                // MIUI MOD
                // (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false, r.callerPackage))
                        == null) {
            // Ah, this recipient is unavailable.  Finish it if necessary,
            // and mark the broadcast record as ready for the next.
            Slog.w(TAG, "Unable to launch app "
                    + info.activityInfo.applicationInfo.packageName + "/"
                    + receiverUid + " for broadcast "
                    + r.intent + ": process is bad");
            logBroadcastReceiverDiscardLocked(r);
            finishReceiverLocked(r, r.resultCode, r.resultData,
                    r.resultExtras, r.resultAbort, false);
            scheduleBroadcastsLocked();
            r.state = BroadcastRecord.IDLE;
            return;
        }

app遲遲收不到有序廣播

廣播隊(duì)列中存在很多活躍狀態(tài)的有序廣播一直未被分發(fā),所以app未接收到對(duì)應(yīng)廣播

  Active Ordered Broadcast background #68:
    BroadcastRecord{4397e66 u0 null} to user 0
    Intent { flg=0x10 cmp=android.app.stubs/.CommandReceiver (has extras) }
      extras: Bundle[mParcelledData.dataSize=280]
    caller=android.app.stubs 0:android.app.stubs/u0a553 pid=9147 uid=10553
    enqueueClockTime=2022-07-21 04:55:24.794 dispatchClockTime=1969-12-31 16:00:00.000 //分發(fā)時(shí)間無效代表未被分發(fā)
    dispatchTime=-- (3h46m21s924ms since enq) receiverTime=-
    resultTo=android.content.IIntentReceiver$Stub$Proxy@9e215a7 resultCode=-1 resultData=null
    resultAbort=false ordered=true (有序) sticky=false initialSticky=false
    ....

查看下當(dāng)前正在派發(fā)的有序廣播是誰(shuí)

  In-Flight Ordered Broadcast background #0: // 后臺(tái)廣播隊(duì)列正在派發(fā)的有序廣播
    BroadcastRecord{619f958 u0 android.intent.action.PACKAGE_CHANGED} to user 0 // 這個(gè)廣播可以派發(fā)給處于stopped狀態(tài)的app
    Intent { act=android.intent.action.PACKAGE_CHANGED dat=package:com.android.test.notificationapp flg=0x5000010 cmp=com.smile.gifmaker/com.yxcorp.gifshow.ad.detail.AppInstalledReceiver (has extras) }
      extras: Bundle[{android.intent.extra.changed_component_name=com.android.test.notificationapp, android.intent.extra.DONT_KILL_APP=true, android.intent.extra.UID=10258, android.intent.extra.changed_component_name_list=[com.android.test.notificationapp], android.intent.extra.user_handle=0, android.intent.extra.REASON=android.intent.action.OVERLAY_CHANGED}]
    caller=null null pid=1568 uid=1000
    enqueueClockTime=2022-07-21 17:39:13.363 dispatchClockTime=2022-07-21 17:42:24.760  // 分發(fā)也比較晚
    dispatchTime=-2s650ms (+3m11s397ms since enq) receiverTime=-2s643ms
    resultTo=null resultCode=0 resultData=null
    nextReceiver=3(//當(dāng)前派發(fā)索引為3,即已派發(fā)到第三個(gè)receiver) receiver=android.os.BinderProxy@c387ee9
    curReceiver=ActivityInfo{8030e35 com.yxcorp.gifshow.ad.detail.AppInstalledReceiver} //當(dāng)前派發(fā)到的receiver
    curApp=ProcessRecord{5182ab1 10877:com.smile.gifmaker/u0a225}  // 當(dāng)前派發(fā)到快手進(jìn)程
    curComponent={com.smile.gifmaker/com.yxcorp.gifshow.ad.detail.AppInstalledReceiver}
    curSourceDir=/data/app/partner-com.smile.gifmaker_13/base.apk
    state=1 (APP_RECEIVE)  // 廣播狀態(tài)不置為idle,后續(xù)的有序廣播無法分發(fā)
    Deliver +4ms #0: (manifest)
      priority=0 preferredOrder=0 match=0x208000 specificIndex=-1 isDefault=false
      ActivityInfo:
        name=com.android.settings.search.provider.UpdateReceiver
        packageName=com.android.settings
        enabled=true exported=true directBootAware=true
        launchMode=0 flags=0x10000 privateFlags=0x0 theme=0x0
        resizeMode=RESIZE_MODE_RESIZEABLE
        knownActivityEmbeddingCerts={}
    Deliver +3ms #1: (manifest)
      priority=0 preferredOrder=0 match=0x208000 specificIndex=-1 isDefault=false
      ActivityInfo:
        name=com.miui.vsimcore.AppInstallReceiver
        packageName=com.miui.vsimcore
        labelRes=0x7f0b001f nonLocalizedLabel=null icon=0x0 banner=0x0
        enabled=true exported=true directBootAware=false
        launchMode=0 flags=0x10000 privateFlags=0x0 theme=0x0
        resizeMode=RESIZE_MODE_RESIZEABLE
        knownActivityEmbeddingCerts={}
    Deliver 0 #2: (manifest)
      priority=0 preferredOrder=0 match=0x208000 specificIndex=-1 isDefault=false
      ActivityInfo:
        name=com.yxcorp.gifshow.ad.detail.AppInstalledReceiver
        packageName=com.smile.gifmaker
        enabled=true exported=true directBootAware=false
        launchMode=0 flags=0x10000 privateFlags=0x0 theme=0x0
        resizeMode=RESIZE_MODE_RESIZEABLE
        knownActivityEmbeddingCerts={}

接下來看下該進(jìn)程主線程狀態(tài)

// 該app進(jìn)程多次發(fā)生ANR,且type為接收PACKAGE_CHANGED廣播
ANR Reason: Broadcast of Intent { act=android.intent.action.PACKAGE_CHANGED dat=package: flg=0x5000010 cmp=com.smile.gifmaker/com.yxcorp.gifshow.XX.detail.AppInstalledReceiver (has extras) }
Binder Tracsaction Info:

// 主線程一直處于sleep狀態(tài)
BackTrace:
bgAnr=true@@@type=broadcast@@@e9b396c72ab96f1221c7640874275438@@@stack type:valid stack@@@traces:
  at java.lang.Thread.sleep(Native method)
  - sleeping on <0x0542172b> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:450)
  - locked <0x0542172b> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:355)
  at com.yxcorp.gifshow.permission.s.b(kSourceFile:262150)
  at com.yxcorp.gifshow.permission.s.a(kSourceFile:262219)
  at com.yxcorp.gifshow.permission.s.b(kSourceFile:16777231)
  at com.yxcorp.gifshow.permission.s.a(kSourceFile:16777216)
  at com.yxcorp.gifshow.KwaiApp.onBaseContextAttached(kSourceFile:17563681)
  at com.yxcorp.gifshow.AppLike.onBaseContextAttached(kSourceFile:16842766)
  at com.kwai.hotfix.entry.TinkerApplicationInlineFence.handleMessageImpl(kSourceFile:17367174)
  at com.kwai.hotfix.entry.TinkerApplicationInlineFence.handleMessage_$noinline$(kSourceFile:16777219)
  at com.kwai.hotfix.entry.TinkerApplicationInlineFence.handleMessage(kSourceFile:16777216)
  at com.kwai.hotfix.loader.app.TinkerInlineFenceAction.callOnBaseContextAttached(kSourceFile:33751045)
  at com.kwai.hotfix.loader.app.TinkerApplication.onBaseContextAttached(kSourceFile:16842785)
  at com.kwai.hotfix.loader.app.TinkerApplication.attachBaseContext(kSourceFile:16842763)
  at com.yxcorp.gifshow.App.attachBaseContext(kSourceFile:16842796)
  at android.app.Application.attach(Application.java:346)
  at android.app.Instrumentation.newApplication(Instrumentation.java:1233)
  at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1479)
  at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1412)
  at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6895)
  at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2211)
  at android.os.Handler.dispatchMessage(Handler.java:106)

結(jié)論:該app導(dǎo)致后續(xù)有序廣播派發(fā),且應(yīng)用商店更新該app后無此問題

廣播的三千問

  1. 有序廣播的receiver在什么時(shí)候進(jìn)行排序?
  2. 廣播的入隊(duì)和分發(fā)流程是同步還是異步?
  3. 靜態(tài)注冊(cè)的receiver(有序&無序)對(duì)應(yīng)的廣播入有序廣播隊(duì)列還是無序(并行)廣播隊(duì)列?
  4. 廣播有哪些狀態(tài)?分別代表什么?
  5. receiver有哪些狀態(tài),分別代表什么?
  6. 廣播過程中涉及到哪些超時(shí)邏輯?
  7. 系統(tǒng)有幾個(gè)廣播隊(duì)列?
  8. 如何發(fā)送有序廣播?粘性廣播?前臺(tái)廣播?
  9. android O 加入了后臺(tái)啟動(dòng)限制,主要限制邏輯是?
  10. 什么情況下發(fā)送的廣播不會(huì)入隊(duì)?
  11. 通過廣播Receiver拉起進(jìn)程的進(jìn)程優(yōu)先級(jí)有什么特點(diǎn)?
  12. 如何通過log查看廣播的是否分發(fā)超時(shí)?以及廣播是否派給對(duì)應(yīng)的receiver?分發(fā)狀態(tài)?
  13. 如果通過log查看廣播是否是有序廣播?靜態(tài)注冊(cè)?
  14. 哪個(gè)廣播享受總分發(fā)超時(shí)豁免?
  15. 哪些隱式廣播可以派發(fā)給后臺(tái)進(jìn)程?
  16. 有序廣播可以通過什么傳遞數(shù)據(jù)?無序廣播呢?
  17. 有序廣播的resultTo(最終接收者)的派發(fā)時(shí)機(jī)?
  18. 發(fā)送受保護(hù)的廣播有什么限制?
  19. 發(fā)送sticky廣播有什么限制?
  20. 如何做到廣播只派發(fā)給動(dòng)態(tài)注冊(cè)的receiver?
  21. 派發(fā)靜態(tài)廣播時(shí),進(jìn)程不存在,如何處理?
  22. 廣播入隊(duì)前如何獲取靜態(tài)receivers?如何獲取動(dòng)態(tài)receivers?
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容