BroadcastReceiver 静态注册与动态注册-《Android深水区(七)》

BroadcastReceiver 静态注册与动态注册

前言

BroadcastReceiver 很容易被误用成"轻量消息总线"或"后台唤醒入口"。这两个理解都不完整。

它真正解决的问题是:系统或应用中发生了某个事件,Android 需要把一个 Intent 分发给符合条件的接收者,并在很短时间内完成回调。这个模型适合电量变化、开机完成、下载完成、自定义应用内事件、短信验证码、蓝牙状态等"事件通知",不适合承载长任务。

线上广播问题通常集中在四类:

  • Manifest 里声明了接收器,但 Android 8.0+ 设备收不到大多数隐式系统广播。
  • 动态注册忘记在正确生命周期注销,Activity 退出后泄漏 Context。
  • targetSdk 升到 Android 14 后,registerReceiver() 因缺少 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTEDSecurityException
  • onReceive() 里做网络、数据库或大 JSON 解析,主线程卡顿,甚至触发广播 ANR。

本文按"使用方式 -> 版本规则 -> Framework 调用链 -> 工程实践"的顺序重建 BroadcastReceiver 的模型。版本结论以 2026-07-01 查阅的 Android Developers 文档和 AOSP refs/heads/main 为准。

前置知识

建议先掌握:

  • A019:Android 应用进程、组件与系统架构概览。
  • A020:Activity 生命周期完整解析。
  • A023:Intent 显式、隐式与 PendingIntent。
  • A024:Service、前台服务与后台限制。
  • Manifest 组件声明、IntentFilter、主线程、进程优先级和后台执行限制。

A023 解释了 Intent 如何描述一次请求。本文继续回答:这个 Intent 如果是广播,系统如何找到接收者、如何分发、为什么有些广播不能再靠 manifest 静态接收。

核心概念

BroadcastReceiver 是短回调组件

BroadcastReceiver.onReceive(context, intent) 默认在应用主线程执行。官方 API 文档和 AOSP BroadcastReceiver.java 都明确提醒:主线程中不能执行长耗时操作;即使使用 goAsync() 把工作移到后台线程,也仍然受广播执行时限约束,并且必须调用 PendingResult.finish()

所以它适合做三件事:

  • 校验 intent.action、读取少量 extras。
  • 更新轻量内存状态或发送应用内事件。
  • 把长任务转交给 WorkManager、JobScheduler、前台服务或业务队列。

它不适合做这些事:

  • onReceive() 中直接访问网络。
  • 同步读写大文件或数据库。
  • 试图通过大量隐式广播长期保活。
  • 把自定义跨应用广播当成无需权限的公开接口。

静态注册与动态注册

维度 静态注册 动态注册
入口 AndroidManifest.xml<receiver> Context.registerReceiver()
生命周期 由系统按 manifest 发现,可冷启动进程 跟注册时使用的 Context 生命周期相关
适合场景 少量必须由系统唤醒的事件,例如允许的系统广播、显式发给本应用的广播 UI 可见期间监听、应用进程内事件、只在登录/连接期间有效的事件
Android 8.0+ 限制 target 26+ 不能用 manifest 接收大多数隐式广播,例外清单除外 用户正在使用应用时仍可注册接收
Android 13/14 安全 Manifest 接收器依赖 android:exported、permission 等声明 Android 13 引入 exported flags;target 34+ 多数动态注册必须显式传 flag
常见风险 误以为能接收所有系统广播;组件暴露面过大 忘记注销;flag 选错导致安全漏洞或收不到广播

选择规则很简单:需要系统在进程不活跃时仍能把事件投递给你,才考虑静态注册;只在某个页面、会话或进程存活期间有意义,优先动态注册。

有序广播、普通广播与粘性广播

类型 API 特点 现代建议
普通广播 sendBroadcast() 系统可并行或按策略分发,接收者不能依赖全局顺序 常用,但要限制 action 范围和权限
有序广播 sendOrderedBroadcast() 接收者可读写结果并影响后续接收者 谨慎使用,避免把业务正确性建立在优先级上
粘性广播 sendStickyBroadcast() 最后一次广播会被系统保留 已废弃,不应用作状态存储

广播不是可靠消息队列。需要"至少一次执行""失败重试""网络约束""进程死亡后恢复"的任务,应该交给 WorkManager 或服务端任务系统。

整体架构

BroadcastReceiver 的分发跨过 App 进程和 system_server。动态注册和静态注册在入口不同,但最终都围绕 BroadcastRecord、广播队列和应用进程回调运转。

flowchart TD Sender[&#34;发送方: sendBroadcast&#34;] AMS[&#34;system_server: ActivityManagerService&#34;] Queue[&#34;BroadcastQueueModernImpl&#34;] Record[&#34;BroadcastRecord&#34;] Static[&#34;Manifest receiver: ResolveInfo&#34;] Dynamic[&#34;Registered receiver: BroadcastFilter&#34;] AppThread[&#34;App: ActivityThread / LoadedApk&#34;] Receiver[&#34;BroadcastReceiver.onReceive&#34;] Work[&#34;WorkManager / JobScheduler / FGS&#34;] Sender --> AMS AMS --> Queue Queue --> Record Record --> Static Record --> Dynamic Static --> AppThread Dynamic --> AppThread AppThread --> Receiver Receiver --> Work

这张图的关键点是:接收器不是直接被发送方调用。发送方把 Intent 交给系统,系统解析接收者、建立 BroadcastRecord、进入广播队列,然后通过应用进程的 ActivityThread 或注册时保存的 IIntentReceiver 回调到 onReceive()

工作流程

静态注册流程

静态注册的接收器写在 manifest 中:

xml 复制代码
<receiver
    android:name=".boot.BootCompletedReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

系统在解析包信息时知道这个接收器。广播发送后,如果 Intent 能匹配到该接收器,系统可能启动应用进程,再调用接收器。对开发者来说,这种方式的价值是"应用没打开时也可能收到事件";代价是它受后台限制、安全声明和系统例外清单严格约束。

从 Android 8.0 API 26 开始,target 26+ 的应用不能在 manifest 中声明大多数隐式广播接收器,除非该广播属于官方例外列表,或广播显式指向你的应用。官方这样做的核心原因是减少大量应用被同一系统事件同时唤醒,降低后台资源消耗。

动态注册流程

动态注册在运行时完成:

kotlin 复制代码
private val timeTickReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_TIME_TICK) {
            viewModel.refreshClock()
        }
    }
}

override fun onStart() {
    super.onStart()
    val filter = IntentFilter(Intent.ACTION_TIME_TICK)
    ContextCompat.registerReceiver(
        this,
        timeTickReceiver,
        filter,
        ContextCompat.RECEIVER_NOT_EXPORTED,
    )
}

override fun onStop() {
    unregisterReceiver(timeTickReceiver)
    super.onStop()
}

动态注册的核心不是"更高级",而是"作用域更清晰"。上面的接收器只在 Activity 可见期间监听分钟变化,页面停止后立即注销,既避免泄漏,也避免后台无意义工作。

Android 13/14 的动态注册 flag

Android 13 引入了更安全的 context-registered receiver exported 配置。Android 14 进一步要求 target 34+ 的应用在注册非纯系统广播接收器时,必须指定 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED,否则会抛 SecurityException

选择方式:

场景 flag
只接收本应用发送的广播 RECEIVER_NOT_EXPORTED
接收其他普通应用发送的广播 RECEIVER_EXPORTED,同时考虑自定义 permission
接收所有系统广播,包括部分高权限系统应用发出的广播 通常需要 RECEIVER_EXPORTED
只注册系统广播 Android 14 文档说明可不指定 exported flag,但实际项目中建议用 AndroidX 兼容 API,并按官方要求区分

这里最容易犯的错是:为了"省事"全部用 exported。这样会扩大攻击面,尤其是自定义 action 如果没有权限保护,其他应用也可能构造 Intent 触发你的接收逻辑。

API 使用

1. Manifest 静态注册:只做分发,不做长任务

kotlin 复制代码
class BootCompletedReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action != Intent.ACTION_BOOT_COMPLETED) return

        val request = OneTimeWorkRequestBuilder<WarmUpWorker>()
            .setInitialDelay(30, TimeUnit.SECONDS)
            .build()

        WorkManager.getInstance(context.applicationContext)
            .enqueueUniqueWork(
                "boot-warm-up",
                ExistingWorkPolicy.KEEP,
                request,
            )
    }
}

Manifest 中还需要权限:

xml 复制代码
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<receiver
    android:name=".boot.BootCompletedReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

onReceive() 只负责判断 action 和投递任务。真正的网络、数据库、重试和约束交给 WorkManager。

2. 动态注册:跟随生命周期

在 Compose 或现代 Activity 中,不要把注册逻辑散在多个回调里。可以封装为生命周期感知组件:

kotlin 复制代码
class BatteryReceiver(
    private val onBatteryLow: () -> Unit,
) : DefaultLifecycleObserver {

    private var registered = false

    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == Intent.ACTION_BATTERY_LOW) {
                onBatteryLow()
            }
        }
    }

    override fun onStart(owner: LifecycleOwner) {
        val context = (owner as? ComponentActivity) ?: return
        ContextCompat.registerReceiver(
            context,
            receiver,
            IntentFilter(Intent.ACTION_BATTERY_LOW),
            ContextCompat.RECEIVER_NOT_EXPORTED,
        )
        registered = true
    }

    override fun onStop(owner: LifecycleOwner) {
        if (registered) {
            val context = owner as? ComponentActivity ?: return
            context.unregisterReceiver(receiver)
            registered = false
        }
    }
}

真实项目里可以进一步把 Context 作为构造参数传入,并使用 applicationContext 或 Activity Context 明确作用域。原则是:在哪里注册,就要能证明在哪里注销。

3. 自定义应用内广播:优先显式、加权限

如果仍要用广播做模块间通知,至少让 Intent 显式指向本应用:

kotlin 复制代码
private const val ACTION_SYNC_STATE_CHANGED =
    "com.example.app.action.SYNC_STATE_CHANGED"

fun Context.sendSyncStateChanged(state: String) {
    val intent = Intent(ACTION_SYNC_STATE_CHANGED)
        .setPackage(packageName)
        .putExtra("state", state)

    sendBroadcast(intent)
}

跨应用广播要额外考虑发送权限和接收权限。不要把用户 ID、token、定位、设备标识等敏感信息直接放进公开广播 extras。

源码分析

版本与源码路径

本文参考 AOSP refs/heads/main

路径 作用
BroadcastReceiver frameworks/base/core/java/android/content/BroadcastReceiver.java 定义 onReceive()goAsync()PendingResult.finish()
Context frameworks/base/core/java/android/content/Context.java 定义 registerReceiver()RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED
ContextImpl frameworks/base/core/java/android/app/ContextImpl.java 动态注册实现入口,调用 AMS
LoadedApk.ReceiverDispatcher frameworks/base/core/java/android/app/LoadedApk.java 保存动态注册 receiver 与 IIntentReceiver 桥接对象
ActivityThread frameworks/base/core/java/android/app/ActivityThread.java 应用进程中调度静态和动态 receiver 回调
BroadcastQueueModernImpl frameworks/base/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java system_server 中的广播队列调度
BroadcastRecord frameworks/base/services/core/java/com/android/server/am/BroadcastRecord.java 保存 Intent、接收者列表、分发状态、超时统计

动态注册调用链

sequenceDiagram participant App as App Context participant CI as ContextImpl participant LA as LoadedApk participant AMS as ActivityManagerService participant BQ as BroadcastQueue participant RD as ReceiverDispatcher participant BR as BroadcastReceiver App->>CI: registerReceiver(receiver, filter, flags) CI->>LA: getReceiverDispatcher(receiver, scheduler) CI->>AMS: registerReceiverWithFeature(...) AMS->>BQ: 保存 BroadcastFilter BQ-->>RD: performReceive(intent) RD-->>BR: onReceive(context, intent)

ContextImpl.registerReceiverInternal() 会为普通 BroadcastReceiver 找到或创建 LoadedApk.ReceiverDispatcher,拿到内部 IIntentReceiver,再通过 ActivityManager.getService().registerReceiverWithFeature() 注册到系统服务。后续广播到达时,system_server 根据注册信息找到目标进程,再通过这个 Binder 回调回来。

这个设计解释了两个常见现象:

  • 动态 receiver 跟注册对象相关,忘记注销会留下 ReceiverDispatcher 和 Context 关联,导致泄漏。
  • 如果注册时传入 Handler scheduleronReceive() 可以被调度到指定线程;不传时默认走主线程。

静态接收调用链

sequenceDiagram participant AMS as system_server participant BQ as BroadcastQueueModernImpl participant AT as ActivityThread participant App as Application participant R as BroadcastReceiver AMS->>BQ: enqueueBroadcastLocked(record) BQ->>BQ: scheduleReceiverCold/WarmLocked BQ->>AT: scheduleReceiver(intent, activityInfo, ...) AT->>App: getReceiverRestrictedContext() AT->>R: onReceive(context, intent) R-->>AT: return or goAsync().finish() AT-->>BQ: finishReceiver

静态接收器在 BroadcastRecord.receivers 中通常表现为 ResolveInfo;动态接收器通常是 BroadcastFilterBroadcastQueueModernImpl 会把广播放入按进程组织的队列,冷进程可能触发进程启动,热进程则直接调度。AOSP 源码中 scheduleReceiverWarmLocked() 会把广播状态标记为 DELIVERY_SCHEDULED,然后调用应用进程的 scheduleReceiver() 或动态注册 receiver 的 performReceive()

BroadcastRecord 保存了什么

BroadcastRecord 是理解广播分发状态的核心对象。它保存:

  • 原始 Intent 和目标组件。
  • 发送方包名、uid、pid。
  • 接收者列表,元素可能是静态接收器 ResolveInfo 或动态接收器 BroadcastFilter
  • orderedstickytimeoutExemptresultCoderesultData 等分发属性。
  • 每个接收者的 delivery 状态,例如 PENDINGSCHEDULEDDELIVEREDSKIPPEDTIMEOUTFAILURE
  • enqueueTimedispatchTimereceiverTimefinishTime 等时间指标。

这说明广播不是简单的 forEach(receiver) receiver.onReceive()。系统会记录状态、跳过策略、超时、进程冷启动、ordered broadcast 的阻塞关系和统计信息。

线程和进程边界

阶段 进程 线程/调度
发送广播 发送方 App 或 system_server 调用方线程进入 Binder
匹配和排队 system_server AMS/BroadcastQueue 持锁维护状态
冷启动静态接收器 system_server -> 目标 App 可能启动目标进程
调用 onReceive() 目标 App 默认主线程;动态注册可指定 Handler
goAsync().finish() 目标 App -> system_server 后台线程可 finish,但仍受广播时限

因此,不要在 onReceive() 里持有锁等待其他主线程任务,也不要在 ordered broadcast 中做慢操作。慢 receiver 会阻塞后续分发,并可能把问题放大到系统级事件延迟。

实战案例

案例:登录态变化通知多个页面

假设旧项目用全局广播通知登录态变化:

kotlin 复制代码
fun Context.broadcastLoginChanged(loggedIn: Boolean) {
    sendBroadcast(
        Intent("com.example.LOGIN_CHANGED")
            .putExtra("loggedIn", loggedIn),
    )
}

这个写法有两个问题:

  • Intent 是隐式的,其他应用可能构造同名 action。
  • 接收侧如果是 manifest receiver,Android 8.0+ 还会遇到隐式广播限制。

改造方式:

kotlin 复制代码
private const val ACTION_LOGIN_CHANGED =
    "com.example.app.action.LOGIN_CHANGED"

fun Context.notifyLoginChanged(loggedIn: Boolean) {
    sendBroadcast(
        Intent(ACTION_LOGIN_CHANGED)
            .setPackage(packageName)
            .putExtra("loggedIn", loggedIn),
    )
}

class LoginChangedReceiver(
    private val onChanged: (Boolean) -> Unit,
) : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action != ACTION_LOGIN_CHANGED) return
        onChanged(intent.getBooleanExtra("loggedIn", false))
    }
}

如果只在本进程内通信,更现代的方案是 StateFlow、共享 Repository、Navigation result 或进程内事件流。广播适合组件边界较老、模块解耦成本较高或必须与系统/其他应用交互的场景,不应作为默认状态管理工具。

案例:下载完成后刷新数据库

系统 DownloadManager.ACTION_DOWNLOAD_COMPLETE 可以由动态注册 receiver 在页面可见时监听;如果希望无 UI 也处理下载结果,应确认系统广播和业务限制,再把处理转交给 Worker:

kotlin 复制代码
class DownloadCompleteReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action != DownloadManager.ACTION_DOWNLOAD_COMPLETE) return

        val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L)
        if (downloadId <= 0L) return

        val work = OneTimeWorkRequestBuilder<ImportDownloadedFileWorker>()
            .setInputData(workDataOf("download_id" to downloadId))
            .build()

        WorkManager.getInstance(context.applicationContext).enqueue(work)
    }
}

这样做的收益是:receiver 快速返回,导入文件的 IO、失败重试和约束由 Worker 管理。

性能优化

1. 让 onReceive() 像中断处理一样短

把 receiver 当成"事件中断"处理:验证 action、解析少量参数、投递任务、返回。任何不可控耗时都移出 receiver。

不推荐:

kotlin 复制代码
override fun onReceive(context: Context, intent: Intent) {
    val payload = URL(intent.getStringExtra("url")).readText()
    database.record(payload)
}

推荐:

kotlin 复制代码
override fun onReceive(context: Context, intent: Intent) {
    val url = intent.getStringExtra("url") ?: return
    val request = OneTimeWorkRequestBuilder<FetchPayloadWorker>()
        .setInputData(workDataOf("url" to url))
        .build()
    WorkManager.getInstance(context.applicationContext).enqueue(request)
}

2. 减少静态隐式广播唤醒

Android 8.0 的限制背后是性能问题:一个系统事件如果唤醒几十个应用,会造成 CPU、内存和电量浪费。现代应用应优先使用:

  • WorkManager 处理延迟、约束、重试任务。
  • JobScheduler 处理系统级计划任务。
  • 明确的回调 API,例如网络状态使用 ConnectivityManager.NetworkCallback
  • 前台服务处理用户可见的持续任务。

3. 控制动态注册作用域

动态注册 receiver 的作用域要和业务需要一致:

业务需要 推荐作用域
页面可见才刷新 UI onStart() 注册,onStop() 注销
Activity 存活期间即可 onCreate() 注册,onDestroy() 注销
整个进程期间监听 Application 注册,并提供明确关闭路径
Compose 页面监听 DisposableEffect 中注册和注销

Compose 中可以这样写:

kotlin 复制代码
@Composable
fun BatteryBanner(
    onBatteryLow: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val context = LocalContext.current
    val currentCallback by rememberUpdatedState(onBatteryLow)

    DisposableEffect(context) {
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (intent.action == Intent.ACTION_BATTERY_LOW) {
                    currentCallback()
                }
            }
        }

        ContextCompat.registerReceiver(
            context,
            receiver,
            IntentFilter(Intent.ACTION_BATTERY_LOW),
            ContextCompat.RECEIVER_NOT_EXPORTED,
        )

        onDispose {
            context.unregisterReceiver(receiver)
        }
    }

    Box(modifier)
}

关键点是使用 rememberUpdatedState 避免 receiver 捕获过期 lambda,并在 onDispose 中注销。

4. 用权限保护跨应用广播

如果广播必须跨应用,至少做三层保护:

  • action 使用反向域名,避免与其他应用冲突。
  • 发送时 setPackage()setComponent() 限制目标。
  • 对敏感广播使用 signature 级别 permission 或 sendBroadcast(intent, permission)

RECEIVER_NOT_EXPORTED 是动态 receiver 的默认安全选择。只有确实需要接收外部应用或高权限系统应用广播时,才考虑 exported。

常见问题

Q1:静态注册是不是已经不能用了?

不是。静态注册仍然存在,也仍适合少量系统允许的唤醒场景和显式发给本应用的广播。变化在于:Android 8.0+ 对 target 26+ 应用限制了大多数 manifest 隐式广播,不能再把它当成通用后台入口。

Q2:动态注册一定收不到 App 进程外事件吗?

不是。动态注册的 receiver 可以接收系统或其他应用广播,但前提是你的进程已经存在且 receiver 已注册。它不负责冷启动你的应用。

Q3:goAsync() 能不能把任务跑很久?

不能。goAsync() 只是允许 onReceive() 先返回并把处理挪到其他线程;广播仍处于 active 状态,系统仍期望你快速调用 PendingResult.finish()。长任务应该交给 WorkManager、JobScheduler、Service 或前台服务。

Q4:为什么 target 34 后 registerReceiver 崩了?

如果不是只注册系统广播,target Android 14 API 34+ 时需要指定 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED。可以使用 AndroidX ContextCompat.registerReceiver() 做兼容,并按接收来源选择 flag。

Q5:LocalBroadcastManager 还能用吗?

不建议。LocalBroadcastManager 已废弃。进程内事件优先使用 FlowLiveData、Repository 状态、依赖注入的事件分发器或明确的回调接口。

Q6:广播优先级还能决定全局顺序吗?

不能依赖它做跨应用全局排序。现代 Android 对广播优先级和调度策略有更多限制。即使 ordered broadcast 仍有结果链路,也不应把核心业务正确性建立在其他应用的接收顺序上。

面试考点

  1. BroadcastReceiver 的静态注册和动态注册有什么区别?

    静态注册由 manifest 声明,系统可在匹配广播时启动应用进程;动态注册在运行时调用 registerReceiver(),生命周期跟注册 Context 相关,适合页面或会话期间监听。

  2. Android 8.0 对广播做了什么限制?

    target 26+ 应用不能在 manifest 中声明接收大多数隐式广播,官方例外列表和显式广播除外。动态注册在应用活跃时仍可使用。

  3. Android 14 target 34 下动态注册 receiver 为什么需要 flag?

    为了让 context-registered receiver 的导出行为显式化,降低未保护动态 receiver 被其他应用触发的风险。非纯系统广播注册通常必须指定 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED

  4. onReceive() 运行在哪个线程?

    默认运行在应用主线程。动态注册时如果传入 Handler scheduler,可以调度到指定线程,但仍要遵守广播执行时限。

  5. goAsync() 的作用和限制是什么?

    它返回 PendingResult,允许 onReceive() 返回后继续异步处理,并最终调用 finish()。它不是长任务通道,执行总时长仍受系统限制。

  6. AOSP 中动态注册 receiver 如何回调?

    ContextImpl.registerReceiverInternal() 创建或获取 LoadedApk.ReceiverDispatcher,通过 AMS 注册 IIntentReceiver。广播到达后 system_server 调用 performReceive(),再由 ReceiverDispatcher 调用应用的 onReceive()

  7. AOSP 中静态 receiver 如何被调起?

    系统匹配 manifest receiver 得到 ResolveInfo,构建 BroadcastRecord 并进入 BroadcastQueueModernImpl。目标进程冷启动或热调度后,ActivityThread.scheduleReceiver()/handleReceiver() 创建 receiver 并调用 onReceive()

  8. 为什么不建议用广播做应用内状态总线?

    广播是系统级事件分发机制,存在生命周期、权限、调度、后台限制和性能成本。进程内状态用 Flow、Repository、回调或导航结果更直接、更可测。

总结

BroadcastReceiver 的核心模型可以压缩成四句话:

  • 它是短生命周期事件回调,不是后台任务执行器。
  • 静态注册用于少数需要系统唤醒的场景,Android 8.0+ 对 manifest 隐式广播有限制。
  • 动态注册用于运行时作用域,Android 13/14 后要认真选择 exported flag。
  • 源码层面,广播由 system_server 排队、记录状态、跨 Binder 回到应用进程,onReceive() 默认主线程且必须快速完成。

工程上最稳的实践是:默认动态注册、默认 RECEIVER_NOT_EXPORTED、默认快速返回;只有在明确需要系统唤醒或跨应用通信时,才扩大注册范围和导出面。

扩展阅读

相关推荐
萝卜er1 小时前
ContentProvider 与跨进程数据访问-《Android深水区(八)》
android
萝卜er1 小时前
Service、前台服务与后台限制-《Android深水区(六)》
android
萝卜er2 小时前
Android 存储体系与分区存储-《Android深水区(十二)》
android
会编程的吕洞宾2 小时前
DeepAgents In Action学习(Second)
android·java·学习
太子釢2 小时前
用 AI 完整实现 Github 客户端:基于 KMP + SwiftUI + Compose
android·人工智能·ios
汪海游龙3 小时前
本地源码还是 Maven 版本?Gradle composite build 双轨依赖的正确接法
android·ci/cd·kotlin
k3x1n3 小时前
三星安卓系统BUG排查记录:云闪付、铁路12306、个人所得税、中国移动等App,长期闪退的根本原因分析
android·逆向
网安蟹佬霸4 小时前
CTF Web方向解题全攻略:从信息收集到getshell(附实战payload与脚本)
android·前端·网络·安全·web安全·网络安全·网安
又见情义5 小时前
Android 13 Settings 设置界面选项裁剪(RK3568平台)
android