目录
前言:为什么 BroadcastReceiver 是 Android 通信的 “万能信使”?
一、BroadcastReceiver 基础认知:到底什么是广播?
1.1 一句话搞懂 BroadcastReceiver 的核心作用
步骤 2:在 Manifest 中注册有序广播(指定优先级)
示例 4:LocalBroadcastManager 实现应用内数据同步
示例 5:跨应用广播实现 “应用 A 通知应用 B 更新数据”
步骤 3:应用 B 在 Manifest 中注册广播(允许跨应用接收)
六、Android 8.0 + 广播限制与适配方案(重点避坑)
6.2 适配方案:如何让广播在 Android 8.0 + 正常工作?
7.2 坑点 2:Android 8.0 后自定义广播接收不到
7.3 坑点 3:广播onReceive()中做耗时操作导致 ANR
八、总结:BroadcastReceiver 核心知识点图谱

class 卑微码农:
def __init__(self):
self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
self.发量 = 100 # 初始发量
self.咖啡因耐受度 = '极限'
def 修Bug(self, bug):
try:
# 试图用玄学解决问题
if bug.严重程度 == '离谱':
print("这一定是环境问题!")
else:
print("让我看看是谁又没写注释...哦,是我自己。")
except Exception as e:
# 如果try块都救不了,那就...
print("重启一下试试?")
self.发量 -= 1 # 每解决一个bug,头发-1
# 实例化一个我
我 = 卑微码农()
前言:为什么 BroadcastReceiver 是 Android 通信的 “万能信使”?
刚入门 Android 时,我曾被 “组件间通信” 搞得头大 ——Activity 想通知 Service 执行任务,后台数据更新后要刷新 UI,系统网络变化要及时响应,这些场景到底该用什么实现?

直到接触了 BroadcastReceiver(广播接收器),才发现它的核心价值:跨组件、跨应用的 “消息中转站” 。不管是系统状态变化(如开机、网络切换、电量低),还是应用内组件交互(如登录成功通知、下载完成提醒),广播都能轻松搞定。
但很多开发者用不好广播,要么遇到 “静态广播接收不到”“Android 8.0 后广播失效” 的问题,要么因滥用广播导致内存泄漏、耗电过快。其实广播的逻辑不复杂,关键是分清类型、掌握注册方式、避开系统限制。
本文会把 BroadcastReceiver 的基础概念、类型划分、注册方式、生命周期、实战场景、避坑指南全讲透。包含 10 个完整可运行的原创示例,从基础入门到进阶实战,新手能直接上手,进阶开发者能夯实基础、避开坑点。
建议先收藏,再跟着示例一步步实操,遇到问题可以在评论区交流~
一、BroadcastReceiver 基础认知:到底什么是广播?

1.1 一句话搞懂 BroadcastReceiver 的核心作用
BroadcastReceiver(简称 “广播”)是 Android 四大组件之一,核心作用是接收和传递 “消息事件” ,实现跨组件、跨应用的异步通信。
简单说:广播就像现实中的 “电台广播”—— 发送者(电台)发出特定频率的信号,接收者(收音机)调至对应频率就能收到消息。在 Android 中:
- 发送者:可以是系统(如开机完成、网络变化)、应用内组件(Activity、Service)、其他应用;
- 接收者:BroadcastReceiver 实例,通过 “注册” 监听特定 “动作(Action)”,收到消息后执行对应逻辑;
- 消息:Intent 对象,可携带数据(如网络类型、电量百分比)。
1.2 必须澄清的 3 个常见误解
- 误解 1:BroadcastReceiver 是独立线程?—— 错!广播的
onReceive()方法默认运行在主线程(UI 线程) ,不能做耗时操作(否则会触发 ANR); - 误解 2:广播能实时通信?—— 错!广播是异步通信,发送者发送后无需等待接收者响应,无法保证消息传递的实时性;
- 误解 3:广播越多越好?—— 错!广播是全局事件,滥用会导致系统资源消耗增加、耗电加快,还可能引发安全问题(如敏感数据通过广播泄露)。
1.3 广播的 3 个核心特性
- 跨组件通信:无需直接持有组件引用,Activity、Service、Fragment、Application 之间可随意通信;
- 跨应用通信:支持不同应用间传递消息(如应用 A 发送广播,应用 B 接收并处理);
- 系统事件监听:能接收 Android 系统发出的各类事件广播(如开机、屏幕亮灭、电量变化、网络切换),快速响应系统状态。
1.4 广播的典型适用场景
- 系统状态监听:网络变化、电量低提醒、开机自启、屏幕亮灭、安装 / 卸载应用;
- 应用内通信:登录成功后通知所有组件、下载完成后刷新 UI、Service 后台任务执行结果反馈;
- 跨应用通信:应用 A 分享数据给应用 B、第三方应用接收系统推送的消息、应用间功能调用通知。
二、广播的分类:3 种维度划分,避免用错场景

BroadcastReceiver 的分类维度有 3 种,不同类型的广播适用场景、使用方式差异很大,这是掌握广播的基础。
2.1 按发送方式划分:有序广播 vs 无序广播
这是最核心的分类,决定了广播的传递逻辑和处理顺序。
1. 无序广播(Normal Broadcast)
- 核心特点:异步、无顺序、不可中断 。所有注册了对应 Action 的接收者都会收到广播,接收顺序不确定,无法阻止其他接收者接收。
- 发送方式:
sendBroadcast(Intent intent); - 优点:效率高,适合无需优先级、无需中断的场景;
- 缺点:无法修改广播数据,无法阻止后续接收者;
- 适用场景:普通通知类消息(如下载完成提醒、登录成功通知)。
2. 有序广播(Ordered Broadcast)
- 核心特点:同步、有顺序、可中断 。接收者按 “优先级” 从高到低依次接收,高优先级接收者可修改广播数据、中断广播(阻止低优先级接收者接收)。
- 发送方式:
sendOrderedBroadcast(Intent intent, String receiverPermission); - 优先级设置:在注册时通过
android:priority属性设置(范围 - 1000~1000,数值越高优先级越高); - 优点:支持优先级排序、数据修改、广播中断,适合需要按顺序处理的场景;
- 缺点:效率低于无序广播,需处理权限问题;
- 适用场景:需要优先级处理的消息(如系统通知、权限申请结果反馈)。
2.2 按注册方式划分:静态广播 vs 动态广播
注册是广播接收者 “监听消息” 的前提,不同注册方式的生命周期、适用场景完全不同,也是最容易踩坑的地方。
1. 静态广播(Manifest 注册)
- 注册方式:在 AndroidManifest.xml 中通过
<receiver>标签注册,指定要监听的 Action; - 生命周期:应用未启动时也能接收广播(系统会唤醒应用进程);
- 优点:无需手动注册 / 注销,适合监听系统事件(如开机自启);
- 缺点:灵活性低,Android 8.0 后受严格限制(大部分后台静态广播失效);
- 适用场景:系统开机广播、应用安装 / 卸载广播等必须在应用未启动时接收的场景。
2. 动态广播(代码注册)
- 注册方式:在代码中通过
registerReceiver()方法注册,在onDestroy()中通过unregisterReceiver()注销; - 生命周期:与注册组件(如 Activity、Service)绑定,组件销毁前必须注销,否则内存泄漏;
- 优点:灵活性高,可动态注册 / 注销,不受 Android 8.0 后台限制影响;
- 缺点:组件销毁前需手动注销,否则会抛出异常;
- 适用场景:应用内组件通信、临时监听系统事件(如页面可见时监听网络变化)。
2.3 按传播范围划分:全局广播 vs 本地广播
1. 全局广播(Global Broadcast)
- 传播范围:整个系统,所有应用都能接收(只要注册了对应 Action);
- 优点:支持跨应用通信;
- 缺点:安全性低(可能被恶意应用监听或伪造广播)、消耗系统资源;
- 适用场景:跨应用消息传递、系统事件监听。
2. 本地广播(Local Broadcast)
- 传播范围:仅当前应用内部,其他应用无法接收;
- 实现方式:通过
LocalBroadcastManager类发送和注册广播; - 优点:安全性高(避免数据泄露)、效率高(不占用系统全局资源)、无权限限制;
- 缺点:不支持跨应用通信;
- 适用场景:应用内组件间通信(如 Activity 与 Service、Fragment 与 Activity)。
三、广播的核心:注册方式与生命周期(实战示例)
广播的使用核心是 “注册 - 接收 - 处理 - 注销”,不同注册方式的实现逻辑差异很大,下面通过完整示例逐个拆解。
3.1 动态广播(代码注册):灵活可控,优先推荐
动态广播是目前开发中最常用的方式,不受 Android 8.0 后后台广播限制,适合大多数场景。
示例 1:动态广播实现应用内通信(登录成功通知)
场景:用户登录成功后,发送广播通知所有组件(如刷新个人信息、跳转首页)。
步骤 1:创建广播接收器(接收登录成功消息)
public class LoginSuccessReceiver extends BroadcastReceiver {
private static final String TAG = "BroadcastTest";
// 定义广播Action(建议用包名前缀,避免冲突)
public static final String ACTION_LOGIN_SUCCESS = "com.example.broadcast.ACTION_LOGIN_SUCCESS";
// 核心方法:接收广播后触发(运行在主线程)
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "收到登录成功广播");
// 1. 验证Action(避免接收无关广播)
if (ACTION_LOGIN_SUCCESS.equals(intent.getAction())) {
// 2. 接收广播携带的数据
String username = intent.getStringExtra("username");
String token = intent.getStringExtra("token");
Log.d(TAG, "登录用户:" + username + ",Token:" + token);
// 3. 执行后续逻辑(如刷新UI、保存Token)
// 注意:onReceive()不能做耗时操作,最多执行10秒(否则ANR)
Toast.makeText(context, "登录成功!欢迎" + username, Toast.LENGTH_SHORT).show();
}
}
}
步骤 2:在 Activity 中注册 / 注销广播
public class LoginActivity extends AppCompatActivity {
private LoginSuccessReceiver mLoginReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// 1. 初始化广播接收器
initBroadcastReceiver();
// 模拟登录按钮点击
findViewById(R.id.btn_login).setOnClickListener(v -> {
// 登录逻辑(模拟验证成功)
String username = "张三";
String token = "abc123456";
// 2. 发送登录成功广播
sendLoginSuccessBroadcast(username, token);
// 3. 跳转首页
startActivity(new Intent(this, HomeActivity.class));
});
}
// 初始化广播接收器(动态注册)
private void initBroadcastReceiver() {
mLoginReceiver = new LoginSuccessReceiver();
// 创建IntentFilter,指定要监听的Action
IntentFilter filter = new IntentFilter();
filter.addAction(LoginSuccessReceiver.ACTION_LOGIN_SUCCESS);
// 注册广播(参数:接收器实例、IntentFilter)
registerReceiver(mLoginReceiver, filter);
}
// 发送登录成功广播
private void sendLoginSuccessBroadcast(String username, String token) {
Intent intent = new Intent(LoginSuccessReceiver.ACTION_LOGIN_SUCCESS);
// 携带数据(支持基本数据类型、String、Parcelable等)
intent.putExtra("username", username);
intent.putExtra("token", token);
// 发送无序广播
sendBroadcast(intent);
}
// 4. 组件销毁时注销广播(必须!避免内存泄漏)
@Override
protected void onDestroy() {
super.onDestroy();
if (mLoginReceiver != null) {
unregisterReceiver(mLoginReceiver);
}
}
}
步骤 3:运行测试,观察日志
- 点击 “登录” 按钮,发送广播;
- 广播接收器的
onReceive()方法被触发,打印用户信息并弹出 Toast; - 退出 Activity 时,广播被注销,无内存泄漏风险。
关键说明:
- 动态广播的生命周期与注册组件(如 Activity)绑定,组件销毁前必须注销;
IntentFilter是 “过滤条件”,只有 Action 匹配的广播才会被接收;onReceive()运行在主线程,禁止耗时操作(如网络请求、文件读写),如需耗时处理,需启动 Service 执行。
3.2 静态广播(Manifest 注册):兼容系统事件
静态广播在 Android 8.0(API 26)后受到严格限制 —— 系统禁止大部分 “后台静态广播”(即应用未启动时无法接收),仅保留部分系统核心广播(如开机、安装应用)仍支持静态注册。
示例 2:静态广播实现开机自启(系统事件监听)
场景:应用需要在设备开机后自动启动服务(如后台数据同步、推送服务)。
步骤 1:创建广播接收器(接收开机广播)
public class BootCompleteReceiver extends BroadcastReceiver {
private static final String TAG = "BroadcastTest";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "收到开机完成广播");
// 验证Action(系统开机广播Action)
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
// 开机后执行逻辑(如启动服务)
Intent serviceIntent = new Intent(context, SyncService.class);
// Android 8.0后启动后台服务需用startForegroundService()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
Log.d(TAG, "开机后启动同步服务");
}
}
}
步骤 2:在 Manifest 中注册广播(关键步骤)
<!-- 1. 添加开机权限 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 2. 注册静态广播接收器 -->
<receiver
android:name=".BootCompleteReceiver"
android:exported="true"> <!-- 允许接收系统广播 -->
<intent-filter>
<!-- 指定监听的系统Action:开机完成 -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- 3. 注册需要启动的Service -->
<service
android:name=".SyncService"
android:exported="false" />
步骤 3:创建 SyncService(开机后启动的服务)
public class SyncService extends Service {
private static final String TAG = "BroadcastTest";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "SyncService创建,开始后台同步");
// 模拟后台数据同步(需放在子线程)
new Thread(() -> {
try {
Thread.sleep(2000);
Log.d(TAG, "数据同步完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
测试说明:
- 由于开机广播需要设备重启,可通过 Android Studio 的 “Emulator Control” 发送模拟开机广播(无需真实重启);
- 设备开机后,系统会发送
ACTION_BOOT_COMPLETED广播,静态注册的接收器接收后启动 SyncService; - 注意:Android 12 及以上,部分设备可能需要授予 “自启动权限” 才能生效。
关键说明:
- 静态广播无需手动注册 / 注销,但需在 Manifest 中配置完整;
android:exported="true"表示允许接收来自应用外部的广播(如系统广播),否则无法接收;- Android 8.0 后,仅系统核心广播支持静态注册,自定义静态广播需配合 “前台服务” 或 “广播权限” 才能生效。
3.3 广播的生命周期:极简且特殊
BroadcastReceiver 的生命周期比 Activity、Service 简单得多,核心只有两个阶段:
1. 生命周期流程:
注册广播 → 接收广播 → 触发onReceive() → 执行完成 → 接收器实例销毁
2. 核心特点:
- 生命周期极短:
onReceive()方法执行完成后,BroadcastReceiver 实例会被系统立即销毁,无法持有状态; - 无后台存活:接收器不能在
onReceive()之外执行任务,所有逻辑必须在该方法内完成(或启动其他组件执行); - 主线程执行:默认运行在主线程,
onReceive()执行时间不能超过 10 秒,否则会触发 ANR(应用无响应)。
3. 避免生命周期相关坑点:
- 不要在
onReceive()中创建非静态内部类(如 Handler),否则会持有接收器引用,导致内存泄漏; - 不要在
onReceive()中启动 Activity(会弹出弹窗影响用户体验),如需跳转,可添加FLAG_ACTIVITY_NEW_TASK标记; - 不要在
onReceive()中长时间阻塞(如Thread.sleep(20000)),会直接触发 ANR。
四、广播的进阶用法:有序广播、本地广播与跨应用通信

基础的无序广播只能满足简单场景,实际开发中还会用到有序广播、本地广播、跨应用广播等进阶用法,下面结合示例详细讲解。
4.1 有序广播:按优先级处理,支持中断与数据修改
有序广播的核心是 “优先级排序” 和 “数据交互”,高优先级接收者可以修改广播数据,甚至中断广播,阻止低优先级接收者接收。
示例 3:有序广播实现 “消息审核”(优先级与中断)
场景:应用发送一条 “评论消息” 广播,高优先级的 “审核接收器” 先验证内容,合法则放行,非法则中断广播。
步骤 1:定义 3 个广播接收器(不同优先级)
// 1. 高优先级:审核接收器(优先级1000)
public class AuditReceiver extends BroadcastReceiver {
private static final String TAG = "OrderedBroadcastTest";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "审核接收器(高优先级)接收广播");
// 接收广播数据
String comment = intent.getStringExtra("comment");
Log.d(TAG, "待审核评论:" + comment);
// 模拟审核逻辑:包含敏感词则中断广播
if (comment.contains("敏感词")) {
Log.d(TAG, "评论包含敏感词,中断广播");
abortBroadcast(); // 中断广播,低优先级接收器无法接收
Toast.makeText(context, "评论包含敏感词,发送失败", Toast.LENGTH_SHORT).show();
} else {
Log.d(TAG, "评论审核通过,放行广播");
// 可修改广播数据(添加审核标记)
Bundle bundle = new Bundle();
bundle.putString("audit_result", "审核通过");
setResultExtras(bundle); // 保存修改后的数据
}
}
}
// 2. 中优先级:通知接收器(优先级500)
public class NotifyReceiver extends BroadcastReceiver {
private static final String TAG = "OrderedBroadcastTest";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "通知接收器(中优先级)接收广播");
// 获取审核接收器传递的数据
Bundle extras = getResultExtras(true);
String auditResult = extras.getString("audit_result");
String comment = intent.getStringExtra("comment");
Log.d(TAG, "审核结果:" + auditResult + ",评论:" + comment);
// 执行通知逻辑(如发送推送)
}
}
// 3. 低优先级:统计接收器(优先级100)
public class StatisticReceiver extends BroadcastReceiver {
private static final String TAG = "OrderedBroadcastTest";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "统计接收器(低优先级)接收广播");
// 执行统计逻辑(如记录评论数量)
}
}
步骤 2:在 Manifest 中注册有序广播(指定优先级)
<!-- 注册有序广播接收器,通过priority指定优先级 -->
<receiver
android:name=".AuditReceiver"
android:exported="false">
<intent-filter android:priority="1000"> <!-- 最高优先级 -->
<action android:name="com.example.broadcast.ACTION_COMMENT_SEND" />
</intent-filter>
</receiver>
<receiver
android:name=".NotifyReceiver"
android:exported="false">
<intent-filter android:priority="500"> <!-- 中优先级 -->
<action android:name="com.example.broadcast.ACTION_COMMENT_SEND" />
</intent-filter>
</receiver>
<receiver
android:name=".StatisticReceiver"
android:exported="false">
<intent-filter android:priority="100"> <!-- 低优先级 -->
<action android:name="com.example.broadcast.ACTION_COMMENT_SEND" />
</intent-filter>
</receiver>
步骤 3:发送有序广播并测试
public class CommentActivity extends AppCompatActivity {
private EditText etComment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comment);
etComment = findViewById(R.id.et_comment);
// 发送有序广播
findViewById(R.id.btn_send_comment).setOnClickListener(v -> {
String comment = etComment.getText().toString().trim();
Intent intent = new Intent("com.example.broadcast.ACTION_COMMENT_SEND");
intent.putExtra("comment", comment);
// 发送有序广播(参数:Intent、接收者权限(null表示无权限))
sendOrderedBroadcast(intent, null);
});
}
}
测试结果:
- 场景 1:评论内容为 “这是一条正常评论”:
D/OrderedBroadcastTest: 审核接收器(高优先级)接收广播 D/OrderedBroadcastTest: 待审核评论:这是一条正常评论 D/OrderedBroadcastTest: 评论审核通过,放行广播 D/OrderedBroadcastTest: 通知接收器(中优先级)接收广播 D/OrderedBroadcastTest: 审核结果:审核通过,评论:这是一条正常评论 D/OrderedBroadcastTest: 统计接收器(低优先级)接收广播 - 场景 2:评论内容为 “这是一条包含敏感词的评论”:
(中、低优先级接收器未接收广播)D/OrderedBroadcastTest: 审核接收器(高优先级)接收广播 D/OrderedBroadcastTest: 待审核评论:这是一条包含敏感词的评论 D/OrderedBroadcastTest: 评论包含敏感词,中断广播
关键说明:
- 优先级范围为
-1000~1000,数值越高,接收顺序越靠前; abortBroadcast()方法只能在有序广播中使用,用于中断广播传递;setResultExtras()和getResultExtras()用于接收者之间传递数据,实现交互。
4.2 本地广播:应用内安全通信,高效无泄漏
本地广播(LocalBroadcast)的传播范围仅限于当前应用,不会被其他应用接收或伪造,安全性高、效率高,适合应用内组件间通信。
示例 4:LocalBroadcastManager 实现应用内数据同步
场景:Service 下载文件完成后,通过本地广播通知 Activity 刷新 UI。
步骤 1:创建本地广播接收器
public class DownloadCompleteReceiver extends BroadcastReceiver {
private static final String TAG = "LocalBroadcastTest";
public static final String ACTION_DOWNLOAD_COMPLETE = "com.example.broadcast.ACTION_DOWNLOAD_COMPLETE";
// 回调接口:通知Activity刷新UI
private OnDownloadCompleteListener mListener;
// 构造方法:传入回调接口
public DownloadCompleteReceiver(OnDownloadCompleteListener listener) {
mListener = listener;
}
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
String filePath = intent.getStringExtra("file_path");
long fileSize = intent.getLongExtra("file_size", 0);
Log.d(TAG, "本地广播:下载完成,路径:" + filePath + ",大小:" + fileSize + "KB");
// 回调Activity刷新UI
if (mListener != null) {
mListener.onComplete(filePath, fileSize);
}
}
}
// 回调接口
public interface OnDownloadCompleteListener {
void onComplete(String filePath, long fileSize);
}
}
步骤 2:在 Activity 中注册 / 注销本地广播
public class DownloadActivity extends AppCompatActivity implements DownloadCompleteReceiver.OnDownloadCompleteListener {
private LocalBroadcastManager mLocalBroadcastManager;
private DownloadCompleteReceiver mDownloadReceiver;
private TextView tvDownloadStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
tvDownloadStatus = findViewById(R.id.tv_download_status);
// 1. 初始化本地广播管理器
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
// 2. 注册本地广播
registerLocalBroadcast();
// 模拟下载按钮点击
findViewById(R.id.btn_start_download).setOnClickListener(v -> {
// 启动下载服务
startService(new Intent(this, DownloadService.class));
tvDownloadStatus.setText("下载中...");
});
}
// 注册本地广播
private void registerLocalBroadcast() {
mDownloadReceiver = new DownloadCompleteReceiver(this);
IntentFilter filter = new IntentFilter();
filter.addAction(DownloadCompleteReceiver.ACTION_DOWNLOAD_COMPLETE);
// 本地广播注册(与全局广播的区别:用LocalBroadcastManager注册)
mLocalBroadcastManager.registerReceiver(mDownloadReceiver, filter);
}
// 实现回调接口:刷新UI
@Override
public void onComplete(String filePath, long fileSize) {
tvDownloadStatus.setText("下载完成!路径:" + filePath + ",大小:" + fileSize + "KB");
}
// 注销本地广播
@Override
protected void onDestroy() {
super.onDestroy();
if (mDownloadReceiver != null) {
mLocalBroadcastManager.unregisterReceiver(mDownloadReceiver);
}
}
}
步骤 3:在 Service 中发送本地广播
public class DownloadService extends Service {
private static final String TAG = "LocalBroadcastTest";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 模拟文件下载(子线程中执行)
new Thread(() -> {
try {
Log.d(TAG, "开始下载文件...");
Thread.sleep(3000); // 模拟下载耗时
// 模拟下载结果
String filePath = "/sdcard/download/test.apk";
long fileSize = 2048; // 2MB
// 发送本地广播(通知下载完成)
Intent broadcastIntent = new Intent(DownloadCompleteReceiver.ACTION_DOWNLOAD_COMPLETE);
broadcastIntent.putExtra("file_path", filePath);
broadcastIntent.putExtra("file_size", fileSize);
// 用LocalBroadcastManager发送本地广播
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
stopSelf(); // 停止服务
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
return START_NOT_STICKY;
}
}
本地广播与全局广播的核心区别:
| 特性 | 本地广播(LocalBroadcast) | 全局广播(Global Broadcast) |
|---|---|---|
| 传播范围 | 仅当前应用 | 整个系统 |
| 安全性 | 高(无泄露 / 伪造风险) | 低(可能被监听 / 伪造) |
| 注册 / 发送方式 | 通过 LocalBroadcastManager | 通过 Context(sendBroadcast) |
| 权限要求 | 无 | 可设置权限限制 |
| 适用场景 | 应用内组件通信 | 跨应用通信、系统事件监听 |
4.3 跨应用广播:实现应用间消息传递
跨应用广播是全局广播的典型用法,允许一个应用发送广播,其他应用注册对应 Action 后接收并处理。
示例 5:跨应用广播实现 “应用 A 通知应用 B 更新数据”
步骤 1:应用 A(发送方)发送跨应用广播
public class SenderActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sender);
// 发送跨应用广播
findViewById(R.id.btn_send_cross_app).setOnClickListener(v -> {
Intent intent = new Intent("com.example.broadcast.ACTION_CROSS_APP_UPDATE");
// 携带数据(跨应用传递)
intent.putExtra("update_data", "应用A的最新数据");
// Android 8.0后跨应用广播需指定包名(否则接收不到)
intent.setPackage("com.example.receiverapp"); // 接收方应用包名
// 发送广播(可设置权限,限制接收方)
sendBroadcast(intent);
Toast.makeText(this, "跨应用广播发送成功", Toast.LENGTH_SHORT).show();
});
}
}
步骤 2:应用 B(接收方)注册广播接收
// 应用B中创建广播接收器
public class CrossAppReceiver extends BroadcastReceiver {
private static final String TAG = "CrossAppTest";
@Override
public void onReceive(Context context, Intent intent) {
if ("com.example.broadcast.ACTION_CROSS_APP_UPDATE".equals(intent.getAction())) {
String updateData = intent.getStringExtra("update_data");
Log.d(TAG, "收到应用A的跨应用广播:" + updateData);
Toast.makeText(context, "收到更新数据:" + updateData, Toast.LENGTH_SHORT).show();
// 执行更新逻辑(如刷新本地数据库)
}
}
}
步骤 3:应用 B 在 Manifest 中注册广播(允许跨应用接收)
<receiver
android:name=".CrossAppReceiver"
android:exported="true"> <!-- 允许接收跨应用广播 -->
<intent-filter>
<action android:name="com.example.broadcast.ACTION_CROSS_APP_UPDATE" />
</intent-filter>
</receiver>
跨应用广播的关键注意事项:
- Android 8.0 后,跨应用广播必须指定接收方包名(
setPackage()),否则系统会视为 “后台广播” 而拦截; - 接收方必须设置
android:exported="true",否则无法接收跨应用广播; - 为了安全,可给广播设置权限:发送方发送时指定权限,接收方必须声明该权限才能接收;
<!-- 发送方和接收方都需在Manifest中声明权限 --> <permission android:name="com.example.broadcast.PERMISSION_CROSS_APP" android:protectionLevel="normal" /> <!-- 接收方需添加权限声明 --> <uses-permission android:name="com.example.broadcast.PERMISSION_CROSS_APP" /> <!-- 发送方发送广播时指定权限 --> sendBroadcast(intent, "com.example.broadcast.PERMISSION_CROSS_APP");
五、系统广播:监听系统状态变化(常用场景汇总)

Android 系统内置了大量广播,用于通知应用系统状态变化。开发中无需自己发送,只需注册对应 Action 即可接收,下面列举最常用的系统广播及用法。
5.1 常用系统广播 Action 与权限
| 系统广播 Action | 触发场景 | 所需权限 | 适用注册方式 |
|---|---|---|---|
| Intent.ACTION_BOOT_COMPLETED | 设备开机完成 | RECEIVE_BOOT_COMPLETED | 静态注册(推荐) |
| Intent.ACTION_CONNECTIVITY_CHANGE | 网络状态变化(连接 / 断开) | 无(Android 6.0 后) | 动态注册 |
| Intent.ACTION_BATTERY_LOW | 电池电量低(通常 < 15%) | 无 | 动态注册 |
| Intent.ACTION_BATTERY_OKAY | 电池电量恢复(>20%) | 无 | 动态注册 |
| Intent.ACTION_SCREEN_ON | 屏幕点亮 | 无 | 动态注册 |
| Intent.ACTION_SCREEN_OFF | 屏幕熄灭 | 无 | 动态注册 |
| Intent.ACTION_PACKAGE_ADDED | 安装新应用 | 无 | 静态 / 动态注册 |
| Intent.ACTION_PACKAGE_REMOVED | 卸载应用 | 无 | 静态 / 动态注册 |
| Intent.ACTION_TIME_TICK | 时间变化(每分钟一次) | 无 | 动态注册 |
示例 6:动态注册监听网络状态变化
public class NetworkChangeReceiver extends BroadcastReceiver {
private static final String TAG = "SystemBroadcastTest";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "收到网络状态变化广播");
// 检查网络状态
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NetworkCapabilities capabilities = cm.getNetworkCapabilities(cm.getActiveNetwork());
if (capabilities != null) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
Toast.makeText(context, "当前网络:WiFi", Toast.LENGTH_SHORT).show();
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
Toast.makeText(context, "当前网络:移动数据", Toast.LENGTH_SHORT).show();
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
Toast.makeText(context, "当前网络:以太网", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "当前无网络连接", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(context, "当前无网络连接", Toast.LENGTH_SHORT).show();
}
} else {
// 兼容Android 6.0以下版本
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Toast.makeText(context, "当前网络:WiFi", Toast.LENGTH_SHORT).show();
} else if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
Toast.makeText(context, "当前网络:移动数据", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(context, "当前无网络连接", Toast.LENGTH_SHORT).show();
}
}
}
}
// 在Activity中注册/注销
public class NetworkMonitorActivity extends AppCompatActivity {
private NetworkChangeReceiver mNetworkReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_network_monitor);
// 注册网络广播
mNetworkReceiver = new NetworkChangeReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_CONNECTIVITY_CHANGE);
registerReceiver(mNetworkReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销广播
unregisterReceiver(mNetworkReceiver);
}
}
关键说明:
- 部分系统广播(如网络变化)在 Android 8.0 后仅支持动态注册,静态注册无法接收;
- 监听网络状态需在 Manifest 中添加 “查看网络状态” 权限(Android 6.0 后无需动态申请):
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - 系统广播的 Action 是系统预定义的,需准确引用(避免拼写错误)。
六、Android 8.0 + 广播限制与适配方案(重点避坑)

Android 8.0(API 26)引入了 “后台执行限制”,对广播的使用做了严格限制,这是很多开发者遇到 “广播失效” 的核心原因。
6.1 Android 8.0 + 广播的核心限制
- 限制 1:后台静态广播失效 。应用未启动时,静态注册的广播接收器无法接收大部分广播(系统核心广播除外,如开机、安装应用);
- 限制 2:后台应用无法接收隐式广播 。隐式广播是指未指定具体组件的广播(如只设置 Action,未设置 Component),后台应用无法接收;
- 限制 3:自定义广播需指定包名或组件 。发送自定义广播时,必须通过
setPackage()或setComponent()指定接收方,否则后台应用无法接收。
6.2 适配方案:如何让广播在 Android 8.0 + 正常工作?
方案 1:动态注册替代静态注册(优先推荐)
对于非系统核心广播,尽量使用动态注册,不受后台限制影响。
方案 2:发送广播时指定接收方包名
发送自定义广播时,通过intent.setPackage("接收方包名")指定接收方,避免隐式广播被拦截。
// Android 8.0+发送自定义广播的正确方式
Intent intent = new Intent("com.example.broadcast.ACTION_CUSTOM");
intent.setPackage("com.example.myapp"); // 指定接收方应用包名
intent.putExtra("data", "测试数据");
sendBroadcast(intent);
方案 3:使用前台服务配合广播
如果需要在应用后台接收广播,可启动前台服务,在服务中动态注册广播(前台服务优先级高,不会被系统轻易杀死)。
方案 4:使用 PendingIntent 发送广播
对于需要延迟发送的广播(如定时任务),可通过PendingIntent发送,不受后台限制影响。
// 使用PendingIntent发送广播
Intent intent = new Intent("com.example.broadcast.ACTION_DELAY");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE
);
// 定时发送(5秒后)
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 5000,
pendingIntent
);
} else {
alarmManager.setExact(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 5000,
pendingIntent
);
}
方案 5:使用系统允许的例外广播
Android 8.0 后仍支持静态注册的系统广播(部分),可在官方文档查询完整列表,常见的有:
ACTION_BOOT_COMPLETED(开机完成)ACTION_LOCKED_BOOT_COMPLETED(锁屏状态下开机完成)ACTION_PACKAGE_ADDED(应用安装)ACTION_PACKAGE_REMOVED(应用卸载)ACTION_BATTERY_LOW(电量低)
七、广播常见坑点与避坑指南(实战经验总结)

7.1 坑点 1:静态广播注册后无法接收
- 现象:在 Manifest 中注册了静态广播,但始终接收不到广播;
- 原因:
- Android 8.0 后,该广播不属于系统例外广播,静态注册被拦截;
- 广播 Action 拼写错误,或未添加
category android:name="android.intent.category.DEFAULT"; - 接收器设置了
android:exported="false",无法接收系统或跨应用广播;
- 解决方案:
- 非系统核心广播,改用动态注册;
- 检查 Action 拼写和 IntentFilter 配置,确保正确;
- 接收系统或跨应用广播时,设置
android:exported="true"。
7.2 坑点 2:Android 8.0 后自定义广播接收不到
- 现象:应用在 Android 8.0 以下正常接收广播,升级到 8.0 + 后失效;
- 原因:Android 8.0 + 限制后台应用接收隐式广播,未指定接收方包名;
- 解决方案:
- 发送广播时通过
setPackage()指定接收方包名; - 改用动态注册或本地广播。
- 发送广播时通过
7.3 坑点 3:广播onReceive()中做耗时操作导致 ANR
- 现象:接收广播后应用无响应,弹出 ANR 提示;
- 原因:
onReceive()运行在主线程,执行时间超过 10 秒; - 解决方案:
- 耗时操作(如网络请求、文件读写)移到 Service 或子线程中执行;
- 如需在广播中启动 Service,Android 8.0 + 需用
startForegroundService();
// 广播中启动Service处理耗时操作 @Override public void onReceive(Context context, Intent intent) { Intent serviceIntent = new Intent(context, MyService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } }
7.4 坑点 4:动态广播未注销导致内存泄漏
- 现象:应用退出后,通过 LeakCanary 检测到内存泄漏;
- 原因:动态注册广播后,未在组件销毁(
onDestroy())时注销,导致接收器持有组件引用; - 解决方案:
- 在 Activity 的
onDestroy()、Service 的onDestroy()中强制注销广播; - 避免在 Application 中动态注册广播(除非手动管理生命周期)。
- 在 Activity 的
7.5 坑点 5:跨应用广播接收不到,权限配置错误
- 现象:应用 A 发送跨应用广播,应用 B 注册后无法接收;
- 原因:
- 应用 B 未声明接收权限,或应用 A 发送时指定了权限但应用 B 未声明;
- 应用 B 的接收器
android:exported="false",禁止接收跨应用广播;
- 解决方案:
- 发送方指定权限时,接收方需在 Manifest 中声明该权限;
- 接收方设置
android:exported="true";
<!-- 接收方声明权限 --> <uses-permission android:name="com.example.broadcast.PERMISSION_RECEIVE" />
7.6 坑点 6:广播传递大数据导致崩溃
- 现象:通过广播传递大型对象(如高清图片、大体积 JSON 数据)时,应用崩溃;
- 原因:Intent 传递数据的大小有限制(通常不超过 1MB),超过限制会抛出
TransactionTooLargeException; - 解决方案:
- 避免通过广播传递大数据,改用文件存储、数据库或 ContentProvider;
- 如需传递对象,仅传递关键信息(如文件路径、ID),而非完整数据。
八、总结:BroadcastReceiver 核心知识点图谱
到这里,BroadcastReceiver 的核心内容已经全部讲完,我们用一张图谱梳理重点:
plaintext
BroadcastReceiver核心知识点
├── 基础认知:跨组件/跨应用消息中转站、3个核心特性、典型场景
├── 分类维度:
│ - 发送方式:有序广播(优先级、中断、数据修改)、无序广播(异步、无顺序)
│ - 注册方式:静态广播(Manifest注册、系统事件)、动态广播(代码注册、灵活可控)
│ - 传播范围:全局广播(跨应用)、本地广播(应用内、安全高效)
├── 生命周期:注册→onReceive()→销毁(极短、主线程执行)
├── 进阶用法:跨应用广播、系统广播监听、PendingIntent发送延迟广播
├── 适配方案:Android 8.0+广播限制、静态转动态、指定接收方包名
├── 避坑指南:ANR、内存泄漏、广播失效、权限错误、大数据传递崩溃
其实广播的学习关键是 “分清场景、选对类型、避开限制”—— 应用内通信优先用本地广播,跨应用通信用全局广播并指定包名,系统事件监听根据需求选静态或动态注册,Android 8.0 + 需重点适配后台限制。
2225

被折叠的 条评论
为什么被折叠?



