深入理解 Android 四大组件之 BroadcastReceiver:从原理到实战(含面试题)

在 Android 四大组件中,BroadcastReceiver(广播接收器)是用于接收和响应系统或应用发出的广播事件的组件。它采用发布 - 订阅模式,能实现跨组件、跨进程的通信,是 Android 系统中解耦组件间交互的重要方式。本文将从基本概念、分类、生命周期、使用方式到实战案例全面解析 BroadcastReceiver,并整理高频面试题,助力开发者夯实基础。

一、BroadcastReceiver 核心概念:什么是广播?

BroadcastReceiver(简称 “广播”)是 Android 系统中一种全局的事件通知机制,其核心作用是接收并处理来自系统或其他应用的广播消息。例如:

  • 系统广播:开机完成、网络状态变化、电池电量低、屏幕点亮 / 熄灭等;
  • 应用广播:应用内自定义事件(如下载完成、数据更新)、跨应用通知等。

广播的核心特点:

  1. 松耦合:发送者无需知道接收者的存在,接收者只需关注自己关心的广播类型;
  2. 全局性:同一广播可被多个接收器同时接收(一对多通信);
  3. 生命周期短暂:Receiver 接收事件后,onReceive()方法执行完毕即销毁,无法执行耗时操作(否则会导致 ANR)。

二、BroadcastReceiver 的分类:按发送方式划分

根据广播的发送方式和传播特性,可分为以下 4 类,核心差异在于传播范围、优先级和是否可被拦截

广播类型发送方法特点适用场景
普通广播(Normal)sendBroadcast()异步传播,所有接收器无序接收,无法拦截或修改数据,效率高一般通知(如下载完成)
有序广播(Ordered)sendOrderedBroadcast()同步传播,按优先级排序接收,高优先级接收器可拦截广播或修改数据需要优先级处理的场景(如权限验证)
本地广播(Local)LocalBroadcastManager.sendBroadcast()仅在应用内传播,无法跨进程,安全性高,效率高应用内组件通信(如 Fragment 通知 Activity)
粘性广播(Sticky)sendStickyBroadcast()发送后会被系统保存,新注册的接收器可接收历史广播(已过时,API 21 起废弃)需保留最新状态的场景(如电量变化)

1. 普通广播(Normal Broadcast)

最常用的广播类型,发送后所有接收器同时接收(无顺序),且无法阻止其他接收器接收,也不能修改广播数据。

// 发送普通广播
Intent intent = new Intent("com.example.MY_NORMAL_BROADCAST");
intent.putExtra("data", "这是普通广播的数据");
sendBroadcast(intent);

2. 有序广播(Ordered Broadcast)

广播按优先级priority,范围 - 1000~1000,值越高优先级越高)依次发送给接收器。高优先级接收器可:

  • 通过abortBroadcast()拦截广播(低优先级接收器无法接收);
  • 通过setResultData()修改广播数据(低优先级接收器接收修改后的数据)。
// 发送有序广播(需权限时可添加第二个参数,如null表示无需权限)
Intent intent = new Intent("com.example.MY_ORDERED_BROADCAST");
intent.putExtra("data", "原始数据");
sendOrderedBroadcast(intent, null);

在 Manifest 中注册时指定优先级:

<receiver
    android:name=".HighPriorityReceiver"
    android:exported="false">
    <intent-filter android:priority="100"> <!-- 高优先级 -->
        <action android:name="com.example.MY_ORDERED_BROADCAST" />
    </intent-filter>
</receiver>

<receiver
    android:name=".LowPriorityReceiver"
    android:exported="false">
    <intent-filter android:priority="50"> <!-- 低优先级 -->
        <action android:name="com.example.MY_ORDERED_BROADCAST" />
    </intent-filter>
</receiver>

高优先级接收器拦截或修改数据:

public class HighPriorityReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取原始数据
        String data = intent.getStringExtra("data"); // 输出:原始数据
        
        // 修改数据(低优先级接收器会收到修改后的数据)
        intent.putExtra("data", "修改后的数据");
        setResultData("额外结果数据"); // 可通过getResultData()获取
        
        // 拦截广播(低优先级接收器无法接收)
        // abortBroadcast();
    }
}

3. 本地广播(Local Broadcast)

仅在当前应用内传播,无法跨进程,因此:

  • 安全性更高(避免其他应用监听或发送恶意广播);
  • 效率更高(无需跨进程通信)。

需通过LocalBroadcastManager管理,用法与普通广播类似:

// 1. 获取LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);

// 2. 注册本地广播接收器
IntentFilter filter = new IntentFilter("com.example.MY_LOCAL_BROADCAST");
localBroadcastManager.registerReceiver(localReceiver, filter);

// 3. 发送本地广播
Intent intent = new Intent("com.example.MY_LOCAL_BROADCAST");
intent.putExtra("msg", "这是本地广播");
localBroadcastManager.sendBroadcast(intent);

// 4. 注销接收器(必须在组件销毁时执行)
@Override
protected void onDestroy() {
    super.onDestroy();
    localBroadcastManager.unregisterReceiver(localReceiver);
}

// 本地广播接收器
private BroadcastReceiver localReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("msg");
        Log.d("LocalBroadcast", msg); // 输出:这是本地广播
    }
};

4. 粘性广播(Sticky Broadcast)

已过时(Android 5.0 API 21 起废弃),原因是安全性差且容易导致内存泄漏。其特点是广播发送后会被系统保存,当新的接收器注册时,可立即收到最近一次的粘性广播。

替代方案:使用LocalBroadcastManager的本地广播,或通过SharedPreferences/ 数据库保存状态,新组件启动时主动读取。

三、BroadcastReceiver 的注册方式:静态与动态

广播接收器必须 “注册” 后才能接收广播,注册方式分为静态注册(Manifest 中声明)和动态注册(代码中注册),两者适用场景不同。

1. 静态注册:Manifest 声明(常驻型)

AndroidManifest.xml中声明接收器,应用安装后即生效,即使应用进程未启动,也能接收广播(系统会唤醒进程)。

适用场景:
  • 接收系统广播(如开机启动、网络变化);
  • 需在应用未运行时响应的事件。
示例:注册网络状态变化接收器
<!-- 1. 声明权限(网络状态需要ACCESS_NETWORK_STATE权限) -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- 2. 注册广播接收器 -->
<receiver
    android:name=".NetworkChangeReceiver"
    android:exported="true"> <!-- 允许接收外部应用广播 -->
    <intent-filter>
        <!-- 系统网络状态变化的action -->
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

接收器实现:

public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 检查网络状态
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
        
        if (isConnected) {
            Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "网络已断开", Toast.LENGTH_SHORT).show();
        }
    }
}
注意:
  • Android 7.0(API 24)后,部分系统广播(如CONNECTIVITY_CHANGE)不再支持静态注册,需改用动态注册或请求权限;
  • android:exported="true"表示允许接收其他应用的广播,若仅接收本应用广播,需设为false(安全最佳实践)。

2. 动态注册:代码中注册(临时型)

在代码(如 Activity、Service)中通过registerReceiver()注册,随组件生命周期生效,组件销毁前必须调用unregisterReceiver()注销,否则会导致内存泄漏。

适用场景:
  • 仅在组件运行时需要接收广播(如 Activity 可见时);
  • 接收 Android 7.0 + 后不支持静态注册的系统广播。
示例:在 Activity 中动态注册时间变化广播
public class MainActivity extends AppCompatActivity {
    private TimeChangeReceiver timeReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 1. 创建接收器实例
        timeReceiver = new TimeChangeReceiver();
        
        // 2. 创建IntentFilter,指定接收的action(系统时间变化)
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_TIME_TICK); // 每分钟触发一次
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 3. 注册广播接收器(在onResume()中注册,与生命周期同步)
        registerReceiver(timeReceiver, filter);
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 4. 注销广播接收器(在onPause()中注销,避免内存泄漏)
        unregisterReceiver(timeReceiver);
    }

    // 时间变化接收器
    private class TimeChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_TIME_TICK.equals(intent.getAction())) {
                Log.d("TimeChange", "当前时间:" + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }
}
注意:
  • 动态注册的接收器优先级高于静态注册(同优先级下);
  • 必须在组件销毁前注销,否则会抛出IllegalArgumentException: Receiver not registered异常。

四、BroadcastReceiver 的生命周期与注意事项

1. 生命周期

BroadcastReceiver 的生命周期极其短暂:

  • 当广播事件触发时,系统创建 Receiver 实例;
  • 调用onReceive(Context, Intent)方法处理事件;
  • 方法执行完毕后,Receiver 实例立即被销毁(生命周期结束)。

核心限制onReceive()方法必须在10 秒内执行完毕,否则会触发 ANR(Application Not Responding)。因此:

  • 不能在onReceive()中执行耗时操作(如下载、数据库写入);
  • 若需处理耗时任务,需通过IntentService(过时)、WorkManager或启动 Service 处理(注意:Android 8.0 + 启动 Service 需用前台 Service)。

2. 注意事项

(1)避免 ANR 的正确做法

onReceive()中需执行耗时操作,应启动后台任务:

@Override
public void onReceive(Context context, Intent intent) {
    // 错误:直接执行耗时操作(会ANR)
    // doLongTimeTask();

    // 正确:启动Service处理(Android 8.0+需用前台Service)
    Intent serviceIntent = new Intent(context, MyBackgroundService.class);
    context.startService(serviceIntent);

    // 或使用WorkManager(推荐)
    OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(MyWorker.class).build();
    WorkManager.getInstance(context).enqueue(work);
}
(2)静态注册的局限性

Android 3.1 + 引入 “应用安装后首次启动前不接收广播” 的机制,避免恶意应用通过广播唤醒未启动的应用。若需接收广播,需用户至少启动一次应用。

(3)权限控制
  • 发送广播时可指定权限:只有声明了该权限的应用才能接收;
  • 接收广播时可指定权限:只有拥有该权限的应用发送的广播才能被接收。

示例:发送带权限的广播

// 发送时指定权限(需在Manifest中声明该权限)
sendBroadcast(intent, "com.example.PERMISSION_SEND_BROADCAST");

在 Manifest 中声明权限并接收:

<!-- 声明权限 -->
<permission android:name="com.example.PERMISSION_SEND_BROADCAST" android:protectionLevel="normal" />
<uses-permission android:name="com.example.PERMISSION_SEND_BROADCAST" />

<!-- 接收时限制发送者必须有该权限 -->
<receiver
    android:name=".MyReceiver"
    android:permission="com.example.PERMISSION_SEND_BROADCAST"> <!-- 仅接收有此权限的广播 -->
    <intent-filter>
        <action android:name="com.example.MY_BROADCAST" />
    </intent-filter>
</receiver>

五、系统常用广播 Action

Android 系统内置了大量广播 Action,用于通知系统状态变化,常用的有:

Action含义所需权限备注(是否支持静态注册)
android.intent.action.BOOT_COMPLETED系统开机完成RECEIVE_BOOT_COMPLETED支持(需权限)
android.net.conn.CONNECTIVITY_CHANGE网络状态变化ACCESS_NETWORK_STATEAndroid 7.0 + 仅支持动态注册
android.intent.action.BATTERY_LOW电池电量低支持
android.intent.action.SCREEN_ON屏幕点亮仅支持动态注册
android.intent.action.SCREEN_OFF屏幕熄灭仅支持动态注册
android.intent.action.PACKAGE_INSTALLED应用安装完成需指定 dataScheme 为 "package"
android.intent.action.TIME_TICK每分钟时间更新仅支持动态注册

六、BroadcastReceiver 高频面试题(含答案)

1. BroadcastReceiver 的两种注册方式有何区别?

对比维度静态注册(Manifest)动态注册(代码中)
生效时机应用安装后立即生效组件(如 Activity)运行时注册后生效
生命周期依赖不依赖组件,应用未运行也能接收依赖组件,组件销毁前必须注销
适用场景系统级事件(如开机启动)组件运行时的临时事件(如屏幕状态变化)
灵活性固定配置,无法动态修改过滤规则可动态修改 IntentFilter,灵活性高
系统限制Android 7.0 + 对部分系统广播有限制无此限制,支持所有广播

2. 普通广播与有序广播的区别?

  • 传播方式:普通广播异步并发,所有接收器同时接收;有序广播同步串行,按优先级依次接收。
  • 拦截与修改:普通广播无法拦截或修改数据;有序广播高优先级接收器可通过abortBroadcast()拦截,或通过setResultData()修改数据。
  • 效率:普通广播效率更高(无顺序等待);有序广播因同步执行,效率较低。
  • 发送方法:普通广播用sendBroadcast();有序广播用sendOrderedBroadcast()

3. 为什么 BroadcastReceiver 的 onReceive () 方法不能执行耗时操作?如何解决?

  • 原因onReceive()运行在主线程(UI 线程),若执行耗时操作(超过 10 秒)会导致 ANR;且 Receiver 生命周期短暂,方法执行完毕后实例会被销毁,耗时任务可能被中断。
  • 解决方式
    1. 启动前台 Service(Android 8.0 + 必须用前台 Service,避免被系统限制)处理耗时任务;
    2. 使用WorkManager(推荐),适合延迟或周期性任务,自动适配系统版本;
    3. 避免使用IntentService(已过时),可改用Coroutine + Service

4. 本地广播(LocalBroadcastManager)的优势是什么?

  1. 安全性高:仅在当前应用内传播,避免其他应用监听或发送恶意广播,防止数据泄露;
  2. 效率更高:无需跨进程通信,减少系统资源消耗;
  3. 生命周期可控:通过LocalBroadcastManager管理注册与注销,避免内存泄漏;
  4. 无需权限:因仅在应用内传播,无需声明权限,简化配置。

5. Android 7.0 后对 BroadcastReceiver 有哪些限制?

  1. 静态注册限制:部分系统广播(如CONNECTIVITY_CHANGEACTION_NEW_PICTURE)不再支持静态注册,需改用动态注册;
  2. 隐式广播限制:禁止静态注册的接收器接收隐式广播(未指定包名的广播),除非广播是系统明确允许的(如BOOT_COMPLETED);
  3. 后台优化:应用进入后台后,动态注册的接收器可能被系统限制接收广播(需结合JobSchedulerWorkManager)。

解决方式

  • 优先使用动态注册接收系统广播;
  • 发送广播时指定包名(显式广播):intent.setPackage(packageName)
  • 非实时任务改用WorkManager

6. 如何确保广播的安全性(防止恶意接收或发送)?

  1. 使用本地广播:通过LocalBroadcastManager发送,仅应用内可接收;
  2. 权限控制
    • 发送广播时指定权限:sendBroadcast(intent, "com.example.MY_PERMISSION"),只有声明该权限的应用可接收;
    • 接收广播时指定权限:在 Manifest 的<receiver>中添加android:permission,只有拥有该权限的应用发送的广播才能被接收;
  3. 显式广播:发送时指定目标组件包名或类名,避免隐式广播被未知应用接收:
  1. Intent intent = new Intent("com.example.MY_ACTION");
    intent.setPackage("com.example.targetapp"); // 指定目标应用包名
    sendBroadcast(intent);
    
  2. 限制导出:静态注册的接收器设置android:exported="false",仅允许接收本应用内的广播。

7. 广播接收器的生命周期是怎样的?

  • 当广播事件触发时,系统创建BroadcastReceiver实例;
  • 调用onReceive(Context, Intent)方法处理事件(此方法必须在 10 秒内完成,否则 ANR);
  • onReceive()执行完毕后,接收器实例立即被销毁,生命周期结束。

注意:接收器中不能持有组件(如 Activity)的强引用,否则会导致组件无法被回收(内存泄漏)。

8. 粘性广播为什么被废弃?

  • 安全性问题:粘性广播会被系统保存,任何应用注册相关接收器都能获取,可能导致敏感数据泄露;
  • 内存泄漏风险:系统需持续保存粘性广播数据,增加内存占用;
  • 功能替代:可通过LocalBroadcastManager(应用内)或SharedPreferences(保存状态)替代,更安全可控。

9. 如何实现跨进程广播通信?

  1. 使用系统广播:发送普通广播或有序广播,其他应用通过静态 / 动态注册接收(需处理权限和显式广播);
  2. 自定义跨进程广播
    • 发送方:通过sendBroadcast()发送显式广播(指定目标应用包名);
    • 接收方:在 Manifest 中静态注册接收器,声明android:exported="true",并指定对应的action
    • 权限控制:通过权限限制只有授权应用才能接收或发送。

示例:应用 A 发送跨进程广播,应用 B 接收

// 应用A发送广播(指定应用B的包名)
Intent intent = new Intent("com.example.CROSS_PROCESS_ACTION");
intent.setPackage("com.example.appb"); // 应用B的包名
sendBroadcast(intent);

// 应用B在Manifest中注册
<receiver
    android:name=".CrossProcessReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.CROSS_PROCESS_ACTION" />
    </intent-filter>
</receiver>

// 应用B的接收器
public class CrossProcessReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("CrossProcess", "收到来自应用A的广播");
    }
}

10. BroadcastReceiver 与 EventBus 的区别?

对比维度BroadcastReceiverEventBus(第三方库)
适用范围系统级、跨应用、应用内通信仅应用内组件通信(无系统级支持)
性能跨进程通信时性能较低(需系统调度)应用内通信性能更高(内存中直接传递)
功能丰富度支持有序、粘性等广播类型支持事件优先级、粘性事件、线程切换等
配置复杂度需注册 / 注销,静态注册需 Manifest 配置仅需注册 / 注销,代码侵入性低
生命周期依赖依赖系统调度,生命周期短暂依赖注册者生命周期,更灵活

总结:系统级或跨应用通信用 BroadcastReceiver;应用内组件解耦通信优先用 EventBus(更轻量高效)。

BroadcastReceiver 作为 Android 四大组件之一,是实现事件通知和跨组件通信的重要工具。开发者需掌握其两种注册方式的差异、四类广播的特性及使用限制,尤其注意 Android 7.0 + 后的系统限制和安全性问题。在实际开发中,应根据场景选择合适的广播类型(如应用内通信优先用本地广播),并结合WorkManager等现代 API 处理后台任务,避免 ANR 和内存泄漏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值