Android Broadcast全面解析

在安卓开发中,广播(Broadcast)是组件间通信的核心机制之一,它像一个"系统公告栏",允许应用内组件、甚至跨应用间通过"发布-订阅"模式传递消息。无论是监听网络变化、接收开机完成事件,还是实现应用内模块通信,广播都扮演着重要角色。但广播的使用看似简单,实则暗藏诸多细节。

一、广播是什么、广播的作用

1. 广播的定义

广播是安卓系统提供的一种跨组件、跨进程的通信机制。其核心思想是"事件驱动":当某个事件发生时(如网络断开、电量低、应用安装),事件源(广播发送者)会发送一条广播消息,系统中所有注册了该广播的接收者(BroadcastReceiver)都会收到这条消息,并执行相应的逻辑。

2. 广播的核心角色

  • 广播发送者(Sender) :发起广播的角色,可以是系统组件(如系统服务)、第三方应用,也可以是自身应用的Activity、Service等。发送广播的本质是通过Intent携带消息,并调用系统API发送。

  • 广播接收者(Receiver) :监听并处理广播的组件,必须继承BroadcastReceiver类,并重写onReceive(Context, Intent)方法------这是接收广播后的核心处理逻辑入口。

  • 系统服务(AMS):Activity Manager Service,作为"中介"负责管理广播的发送与匹配。发送者将广播交给AMS,AMS根据广播的Action、权限等信息,匹配所有符合条件的接收者,再将广播分发给它们。

3. 广播的核心价值

  • 解耦组件通信:发送者无需知道接收者的存在,接收者也无需依赖发送者,二者通过"事件"间接通信,降低组件耦合度。例如,应用的支付模块发送"支付成功"广播,订单模块、消息模块只需注册该广播即可响应,无需与支付模块直接关联。

  • 监听系统事件:这是广播最常用的场景。系统会在特定事件发生时发送预设广播,应用通过接收这些广播实现对系统状态的感知,如监听网络变化、屏幕点亮/熄灭、电池电量等。

  • 跨进程通信:广播天然支持跨进程传递,例如系统的"应用安装完成"广播,所有监听该事件的应用都能收到,实现跨应用的协同。

二、广播的分类

1. 按发送方式:有序广播 vs 无序广播

特性 无序广播(Normal Broadcast) 有序广播(Ordered Broadcast)
传递顺序 所有接收者同时接收,无固定顺序 按接收者优先级排序,从高到低依次传递
截断能力 无法截断,所有接收者都能收到 优先级高的接收者可调用abortBroadcast()截断,后续接收者收不到
数据传递 接收者无法修改广播数据 优先级高的接收者可通过setResultData()修改数据,传递给后续接收者
发送API sendBroadcast(Intent) sendOrderedBroadcast(Intent, String)
适用场景 通知类消息,如"新消息提醒""天气更新" 需要按顺序处理的场景,如短信拦截、权限验证

2. 按作用范围:全局广播 vs 本地广播

全局广播(Global Broadcast):

  • 特性:广播可被系统中所有应用的接收者监听,也可发送给所有应用。
  • 风险:存在安全隐患(如敏感数据通过广播泄露)和性能问题(过多应用监听可能导致广播延迟)。
  • 适用场景:跨应用通信、监听系统事件。

本地广播(Local Broadcast):

  • 特性:广播仅在当前应用内部传递,无法跨进程,由LocalBroadcastManager管理。
  • 优势:安全性高(避免数据泄露)、效率高(无需跨进程通信)、无ANR风险(内部使用Handler机制)。
  • 适用场景:应用内组件间通信,如Activity向Service发送指令、Fragment与Activity传递消息。

3. 按发送者:系统广播 vs 自定义广播

系统广播(System Broadcast):系统预定义的广播,由系统组件发送,对应特定系统事件。例如:

网络变化:android.net.conn.CONNECTIVITY_CHANGE

开机完成:android.intent.action.BOOT_COMPLETED

电量低:android.intent.action.BATTERY_LOW

自定义广播(Custom Broadcast):开发者自行定义Action的广播,用于应用内或跨应用的自定义事件传递。例如,应用内"支付成功"广播,Action可定义为com.example.app.PAY_SUCCESS

三、广播接收者

广播接收者(BroadcastReceiver)是处理广播的核心,其使用流程分为"定义接收者""注册广播""处理广播"三步,其中"注册"是关键------安卓提供两种注册方式,各有适用场景。

1. 定义广播接收者

无论哪种注册方式,接收者的定义逻辑一致:继承BroadcastReceiver,重写onReceive()方法。

java 复制代码
// 自定义广播接收者
public class MyBroadcastReceiver extends BroadcastReceiver {
    // 广播接收后回调此方法
    @Override
    public void onReceive(Context context, Intent intent) {
        // 1. 获取广播携带的数据
        String action = intent.getAction();
        String data = intent.getStringExtra("key");
        
        // 2. 根据Action区分不同广播,执行对应逻辑
        if ("com.example.app.PAY_SUCCESS".equals(action)) {
            // 处理支付成功逻辑:更新订单状态、提示用户等
            Toast.makeText(context, "支付成功:" + data, Toast.LENGTH_SHORT).show();
        } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
            // 处理网络变化逻辑
            checkNetworkState(context);
        }
    }
    
    // 示例:检查网络状态
    private void checkNetworkState(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info != null && info.isConnected()) {
            Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "网络已断开", Toast.LENGTH_SHORT).show();
        }
    }
}

注意:onReceive()方法运行在主线程(UI线程)中,不能执行耗时操作(超过10秒会触发ANR)。若需处理耗时逻辑,需启动Service或使用WorkManager,不能直接在该方法中开线程(线程可能被系统回收)。

2. 注册广播(核心)

广播接收者必须注册后才能接收广播,注册分为"静态注册"和"动态注册",二者的核心差异在于"注册时机"和"生命周期关联"。

方式一:静态注册(AndroidManifest注册)

AndroidManifest.xml中配置接收者,属于"全局注册",应用未启动时也能接收广播。

XML 复制代码
<application ...>
    
    <receiver
        android:name=".MyBroadcastReceiver"  
        android:enabled="true"  
        android:exported="true">  
        <intent-filter>
            
            <action android:name="com.example.app.PAY_SUCCESS" />
            
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>
</application>


<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

说明:

  • enabled:控制接收者是否启用,若为false,即使注册也无法接收广播。
  • exported:控制是否允许接收其他应用的广播。若为false,仅能接收自身应用发送的广播,提升安全性。
  • 适用场景:适用于需要在应用未启动时接收的特殊系统广播,如开机完成、应用安装/卸载等。
  • API 26后的限制:为了优化系统性能,Android8.0(API 26)后,除少数特殊系统广播外静态注册的接收者无法接收普通广播。

方式二:动态注册(代码注册)

在Activity、Service等组件的代码中通过registerReceiver()注册,广播接收者的生命周期与注册组件绑定,组件销毁前必须注销,否则会导致内存泄漏。

java 复制代码
public class MainActivity extends AppCompatActivity {
    private MyBroadcastReceiver myReceiver;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 1. 初始化接收者
        myReceiver = new MyBroadcastReceiver();
        
        // 2. 配置接收的广播Action
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.app.PAY_SUCCESS"); // 自定义广播
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_CHANGE); // 系统广播
        // 若为有序广播,可设置优先级(-1000~1000,数值越大优先级越高)
        intentFilter.setPriority(100);
        
        // 3. 注册广播
        registerReceiver(myReceiver, intentFilter);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 4. 必须注销广播,避免内存泄漏
        unregisterReceiver(myReceiver);
    }
}

注册后,只有当组件(如Activity)处于运行状态时,接收者才能接收广播;组件销毁前必须调用unregisterReceiver()注销,否则系统会抛出IllegalArgumentException。但现在推荐使用 LiveData、SharedFlow(Kotlin)或 EventBus 来替代应用内的本地广播通信。

综上我们可以总结出两种注册方式的区别:

对比维度 静态注册 动态注册
注册时机 应用安装时由系统完成注册 组件运行时(如onCreate)手动注册
应用未启动时 可接收广播(API 26前) 无法接收
生命周期 与应用绑定,除非卸载或禁用 与注册组件绑定,组件销毁需注销
API 26兼容性 大部分广播失效 完全兼容
灵活性 低,无法动态修改 高,可动态添加/移除Action

3. 发送广播

发送广播的核心是通过Intent指定广播的Action和携带数据,再调用对应的发送API。

发送无序广播

java 复制代码
// 1. 构建Intent,指定广播Action
Intent intent = new Intent("com.example.app.PAY_SUCCESS");
// 2. 携带数据(键值对形式)
intent.putExtra("orderId", "123456");
intent.putExtra("amount", 99.0);
// 3. 发送无序广播
sendBroadcast(intent);
// 发送时可以带上权限字符串,只有拥有该权限的接收者才能收到。
sendBroadcast(intent, "com.example.PERMISSION");

// 发送本地无序广播(仅应用内可见)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);

发送有序广播

java 复制代码
Intent intent = new Intent("com.example.app.SMS_RECEIVED");
intent.putExtra("smsContent", "【验证码】123456");
// 发送有序广播,第二个参数是权限(若为null,所有应用均可接收)
// 优先级高的接收者可截断广播
sendOrderedBroadcast(intent, null);
// 有序广播的截断与数据修改示例(在接收者的onReceive中)
@Override
public void onReceive(Context context, Intent intent) {
    if ("com.example.app.SMS_RECEIVED".equals(intent.getAction())) {
        // 1. 修改广播数据
        setResultData("【拦截后】验证码已屏蔽");
        // 2. 截断广播,后续接收者无法收到
        abortBroadcast();
    }
}

四、实践一下:监听系统网络变化

1. 声明权限

XML 复制代码
<!-- 访问网络状态权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

2. 定义广播接收者

java 复制代码
public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 适配API 23+的网络状态获取方式
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Network network = cm.getActiveNetwork();
            NetworkCapabilities capabilities = cm.getNetworkCapabilities(network);
            if (capabilities != null) {
                if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                    sendNetworkEvent(context, "WiFi网络");
                } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                    sendNetworkEvent(context, "移动数据网络");
                } else {
                    sendNetworkEvent(context, "无网络连接");
                }
            } else {
                sendNetworkEvent(context, "无网络连接");
            }
        } else {
            // 兼容低版本
            NetworkInfo info = cm.getActiveNetworkInfo();
            if (info != null && info.isConnected()) {
                if (info.getType() == ConnectivityManager.TYPE_WIFI) {
                    sendNetworkEvent(context, "WiFi网络");
                } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
                    sendNetworkEvent(context, "移动数据网络");
                }
            } else {
                sendNetworkEvent(context, "无网络连接");
            }
        }
    }
    
    // 发送事件到Activity(可通过接口或LiveData优化)
    private void sendNetworkEvent(Context context, String state) {
        if (context instanceof MainActivity) {
            ((MainActivity) context).onNetworkStateChanged(state);
        }
    }
}

3. 动态注册与注销

java 复制代码
public class MainActivity extends AppCompatActivity {
    private NetworkChangeReceiver networkReceiver;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 注册网络变化广播
        networkReceiver = new NetworkChangeReceiver();
        IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(networkReceiver, intentFilter);
    }
    
    // 接收网络状态变化事件,更新UI
    public void onNetworkStateChanged(String state) {
        TextView tvNetwork = findViewById(R.id.tv_network);
        tvNetwork.setText("当前网络:" + state);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销广播
        unregisterReceiver(networkReceiver);
    }
}

结语

广播(Broadcast)作为 Android 四大组件之一,虽然概念诞生已久,但它从未过时。从最早的肆意发送,到如今 Android 8.0 的隐式限制、Android 14 的导出标志强制化,广播机制的每一次演变都折射出 Android 系统对性能与安全的极致追求。

对于开发者而言,掌握广播不仅仅是学会写 onReceive,更重要的是理解其背后的设计模式与系统边界。在实际开发中,我们应遵循"最小权限原则",尽量使用动态注册,善用 SharedFlow 等现代技术替代本地广播,并时刻关注系统版本的迭代差异。

希望这篇文章能帮你构建起完整的广播知识体系。组件通信的方式有千万种,愿你能根据场景,选择最优雅的那一种。Happy Coding!

相关推荐
byc2 小时前
Android 存储目录<内部存储,外部存储app专属,外部存储公共>
android·面试
RollingPin2 小时前
React Native与Flutter的对比
android·flutter·react native·ios·js·移动端·跨平台开发
刘大浪2 小时前
Android studio 开发将gradle 从c盘迁移至自定义盘
android·ide·android studio
装不满的克莱因瓶2 小时前
【2026最新最全】Android Studio安装教程
android·ide·flutter·app·android studio·移动端
2501_916008892 小时前
iOS 能耗检测的工程化方法,构建多工具协同的电量分析与性能能效体系
android·ios·小程序·https·uni-app·iphone·webview
柯南二号3 小时前
【大前端】【Android】获取手机的电池电量、充电状态
android
用户69371750013843 小时前
27.Kotlin 空安全:安全转换 (as?) 与非空断言 (!!)
android·后端·kotlin
Meteors.3 小时前
安卓进阶——原理机制
android·java·开发语言
李坤林3 小时前
Android12 Vsync深度解析VSyncPredictor
android·surfaceflinger