我的安卓第一课:四大组件之一Broadcast

引言

什么是广播

初次听到"广播"这个词时,可能会觉得有些抽象。但如果联想到 IP 协议中的广播机制,或者现实生活中的广播系统------即向特定范围内所有接收者发送一条统一信息的方式,就不难理解其核心含义了。

在 Android 中,广播机制正是基于这一思想构建的:它是一种基于事件驱动的通信方式,允许系统或应用程序发送一条消息(称为"广播"),通知其他组件某些事件的发生。这些事件可以来源于用户的操作、系统状态的变化(如网络连接、电量变化等),也可以是应用内部的业务逻辑触发。

通过注册广播接收器(BroadcastReceiver),应用组件可以在不直接耦合的前提下监听并响应这些事件,从而实现松耦合、高内聚的通信结构。这种机制为组件间协作、跨应用通信以及系统级事件感知提供了强大的支持。

广播能做什么

当系统状态发生改变时,Android 系统会主动发送一系列广播(Broadcast),用于通知应用当前设备环境的变化。通过注册相应的 BroadcastReceiver,应用可以监听这些广播并作出响应,从而实现对系统事件的感知和行为调整。

获取系统状态

以下是几个常见的系统广播示例:

  1. 设备启动完成。当系统完成启动后,会发出 ACTION_BOOT_COMPLETED 广播。应用可以通过监听该广播,在设备重启后执行初始化操作或启动后台服务。

⚠️ 注意:需要声明权限:

Xml 复制代码
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  1. 网络连接状态变化。通过监听 CONNECTIVITY_ACTION 广播,应用可以实时感知网络状态的变化,例如 Wi-Fi 连接、移动数据切换等。这为在网络状态变更时进行数据同步或提示用户提供了基础支持。

  2. 电池电量变化。Android 提供了多个与电池状态相关的广播,如 ACTION_BATTERY_LOW(电量低)和 ACTION_BATTERY_OKAY(电量恢复)。应用可以根据当前电量情况动态调整资源使用策略,例如降低刷新频率或提示用户充电。

  3. 时间变化事件。系统还提供了一些与时间相关的广播,例如每分钟触发一次的 TIME_TICK。开发者可以利用此类广播实现定时提醒、日历更新、按时间展示不同内容等功能

跨应用通信

应用之间可以通过广播机制实现跨进程通信。通过发送一条特定的广播消息,一个应用可以将信息传递给另一个应用;只要目标应用对该广播的 ACTION 进行了监听并注册了相应的 BroadcastReceiver,就可以接收到该广播并作出响应。这种方式为跨应用通信提供了一种轻量级且灵活的解决方案。

不过,使用广播进行跨应用通信的前提是:双方应用必须事先约定相同的广播 Action,并且在必要时声明和授予相应的自定义权限,以确保通信的安全性和可控性。

应用内通信

Android 中的广播机制不仅可用于跨应用通信,同样也适用于应用内部不同组件之间的通信 。通过在应用内发送和接收广播,组件之间可以实现松耦合 的交互方式,无需彼此持有引用 即可完成事件通知与数据传递。这种方式特别适用于需要多个组件监听同一事件、全局状态变更通知等场景=。

不推荐发送大量数据与高频通信

使用广播

在 Android 中,广播的使用主要涉及到两个方面:发送广播和接收广播 。接收广播可以通过两种方式来注册广播接收器(BroadcastReceiver):动态注册和静态注册 。发送广播则分为:发送经典广播与有序广播

接收广播

动态注册监听

动态注册是指在程序运行过程中,通过代码手动注册广播接收器的方式 。这种方式具有更高的灵活性,且从 Android 8.0(API 26)开始,系统对隐式广播的静态注册进行了限制,因此动态注册成为当前推荐的主要方式

第一步:需要实现一个广播接收器 ,继承BroadReceiver,实现onReceive()

java 复制代码
public class TimeChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "时间发生变化", Toast.LENGTH_SHORT).show();
    }
}

onReceive() 方法会在接收到匹配的广播时被调用,注意不要在此执行耗时操作,否则可能导致 ANR。

第二步:在 ActivityService 中注册广播接收器

java 复制代码
binding.buttonRegisterReceiver.setOnClickListener(v -> {
    IntentFilter filter = new IntentFilter();
    // 需要过滤器过滤
    filter.addAction("android.intent.action.TIME_TICK");
    timeChangeReceiver = new TimeChangeReceiver();
    // 当注册的非系统广播时,需要填写是否允许外部应用触发
    // registerReceiver(myTimeBroadcast, filter,  RECEIVER_NOT_EXPORTED);
    registerReceiver(timeChangeReceiver, filter);
});

之后,每过一分钟,应用就会收到一个消息,使用timeChangeReceiver.onReceive(...)接收

需要注意的是,动态注册的广播接收器必须在不再需要时手动注销 ,以避免内存泄漏。通常建议在组件销毁前(如 onDestroy())调用 unregisterReceiver()

静态注册监听

动态注册通过编写代码实现,具有高度的灵活性,尤其适用于运行时根据需求动态控制广播接收器的场景。然而,它也存在一个根本性的限制:只有在应用启动并执行注册操作后,才能开始监听广播。这意味着如果应用未处于活跃状态,将无法接收到相关事件。

与之相对,静态注册不依赖于应用是否启动 ,即使应用当前未运行,只要系统发出匹配的广播,接收器仍会被激活并处理该广播。这使得静态注册非常适合用于监听系统级事件(如设备启动完成、电池状态变化等),在这些场景下,要求应用在特定系统事件发生时能够被自动唤醒并作出响应。

第一步:AndroidManifest.xml注册reveiver

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

第二步:AndroidManifest.xml申请权限 ( 有时需要 )

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

第三步:编写对应接收器

java 复制代码
public class MyStaticReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "设备启动完成", Toast.LENGTH_LONG).show();
    }
}

onReceive() 方法会在接收到匹配的广播时被调用,注意不要在此执行耗时操作,否则可能导致 ANR。

发送

在 Android 中,除了接收广播,应用也可以主动发送广播,以通知其他组件或应用某个事件的发生。根据广播的传递方式和特性,Android 提供了多种广播类型。如下两种

标准广播

标准广播是一种异步广播,系统会将广播同时发送给所有匹配的 BroadcastReceiver。各个接收器之间是并行接收的,彼此之间无法相互影响,也没有优先级之分。

  1. 先注册接收器用于测试
java 复制代码
binding.registerButton.setOnClickListener(view -> {
    IntentFilter filter = new IntentFilter();
    filter.addAction("com.example.hellocast.SEND");
    // 允许外部触发
    registerReceiver(myBroadcast, filter,  RECEIVER_EXPORTED);
    Toast.makeText(this, "MyBroadcast", Toast.LENGTH_SHORT).show();
});
  1. 发送广播
java 复制代码
binding.sendButton.setOnClickListener(view -> {
    Intent intent = new Intent("com.example.hellocast.SEND");
    // 设置指定目标包名,可将隐形广播变为显性广播
    // intent.setPackage(getPackageName());
    intent.putExtra("message", "这是一条标准广播");
    sendBroadcast(intent);
});
  1. 结果
text 复制代码
com.example.hellocast                D  onReceive: com.example.hellocast, 这是一条标准广播

上面有许多注意的点

  1. 静态注册的 BroadcastReceiver 是无法接收隐式广播的,除非携带包名,使得隐式广播变显式广播。(可以自己写代码尝试一下,会发现如果不携带包名,只有动态注册的接收器可以接收广播)
  2. 动态注册接收器无此限制,但当 registerReceiver 的第三个参数为 RECEIVER_NOT_EXPORTED 时,也必须要携带包名才能触发

有序广播

经典广播的广播是并行 的,只要符合要求的接收者都能接收。而有序广播是串行的,必须前接收器允许向后传输,后接收器才能接收到。

发送广播

只需要将sendBroadcast改为sendOrderedBroadcast第二个参数为所要求的权限

java 复制代码
binding.sendButton.setOnClickListener(view -> {
    Intent intent = new Intent("com.example.hellocast.SEND");
    // 转为显式
    intent.setPackage(getPackageName());
    intent.putExtra("message", "这是一条标准广播");
    sendOrderedBroadcast(intent, null);
});

接收广播

实际上广播接收器不进行修改就可以使用。不过这就体现不出有序广播的特点(按优先级广播 )了。如果需要设置优先级,可以使用filter.setPriority(100);<intent-filter android:priority="1000">。例如 动态注册

java 复制代码
binding.registerButton.setOnClickListener(view -> {
    IntentFilter filter = new IntentFilter();
    filter.addAction("com.example.hellocast.SEND");
    filter.setPriority(100);  // 设置优先级
    registerReceiver(myBroadcast, filter,  RECEIVER_EXPORTED);
    Toast.makeText(this, "MyBroadcast", Toast.LENGTH_SHORT).show();
});

静态注册

java 复制代码
<receiver
    android:name=".broadcast.MyReceiver2"
    android:enabled="true"
    android:exported="true">
    <intent-filter android:priority="1000">
        <action android:name="com.example.hellocast.SEND" />
    </intent-filter>
</receiver>

如果需要中止有序广播的向后传播,可以使用abortBroadcast();来中止

java 复制代码
@Override
public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "onReceive: com.example.hellocast.SEND");
    abortBroadcast();
}

广播的权限机制

在安卓系统中,曾经出现过许多"恶意软件"利用广播机制来"作恶"的现象,某些"恶意软件"可能利用广播机制来监听敏感信息、启动未经授权的服务或执行其他形式的攻击。为了防止这种情况的发生,Android 提供了严格的权限机制来控制广播的发送和接收

系统广播

在系统广播中,由于部分系统广播往往涉及到重要信息,故大部分情况下,接收系统广播都需要申请权限 。例如,要接收设备启动完成后的广播 (ACTION_BOOT_COMPLETED),你需要在 AndroidManifest.xml 文件中添加以下权限声明:

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

具体可以查阅developer.android.google.cn/reference/a...

自定义广播

除了接收系统广播外,开发者还可以创建自己的广播来实现应用内部或跨应用之间的通信。为了确保这些广播的安全性,可以通过设置权限来限制哪些应用可以发送或接收它们。

发送广播时指定权限

当你发送一个自定义广播时,可以通过 sendBroadcast(intent, receiverPermission) 方法来限制接收者的权限。例如:

Java 复制代码
Intent intent = new Intent("com.example.CUSTOM_ACTION");
intent.putExtra("message", "这是一个受保护的广播");

// 只有拥有 com.example.permission.RECEIVE_CUSTOM_BROADCAST 权限的应用才能接收
sendBroadcast(intent, "com.example.permission.RECEIVE_CUSTOM_BROADCAST");

接收广播时声明权限

为了让应用能够接收带有权限要求的广播,你必须在 AndroidManifest.xml 文件中声明所需的权限:

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

并且,在注册广播接收器时,可以在 标签中指定权限:

xml 复制代码
<receiver android:name=".MyCustomReceiver"
          android:permission="com.example.permission.RECEIVE_CUSTOM_BROADCAST">
    <intent-filter>
        <action android:name="com.example.CUSTOM_ACTION" />
    </intent-filter>
</receiver>

附录

项目 动态注册 静态注册
注册位置 Java/Kotlin 代码中 AndroidManifest.xml
是否需要应用启动
生命周期控制 手动管理(注册/注销) 系统自动激活
兼容性 支持所有广播类型 部分隐式广播受限于 Android 8.0+
推荐用途 运行时监听、组件间通信 监听系统事件、跨应用广播
安全性 更高(生命周期可控) 稍低(可能被其他应用触发)
组合方式 说明 推荐程度
显式广播 + 动态注册 安全、灵活,推荐用于应用内部通信 ✅ 强烈推荐
隐式广播 + 动态注册 安全且兼容性好,是 Android 8.0+ 推荐方式 ✅ 强烈推荐
显式广播 + 静态注册 不常见,显式广播一般不需要静态注册 ⚠️ 少数情况可用
隐式广播 + 静态注册 从 Android 8.0 开始限制较多,仅几个系统广播可用,建议避免 ❌ 不推荐

系统广播

安卓系统广播列表:<Android SDK>/platforms/<任意android api版本>/data/broadcast_actions.txt

在Android 8.0系统之后,所有隐式广播都不允许使用静态注册的方式来接收了 。隐式广播指的是那些没有具体指定发送给哪个应用程序的广播,大多数系统广播属于隐式广播,但是少数特殊的系统广播目前仍然允许使用静态注册的方式来接收 。这些特殊的系统广播列表详见developer.android.google.cn/develop/bac...

相关推荐
一只柠檬新1 小时前
Web和Android的渐变角度区别
android
志旭1 小时前
从0到 1实现BufferQueue GraphicBuffer fence HWC surfaceflinger
android
_一条咸鱼_1 小时前
Android Runtime堆内存架构设计(47)
android·面试·android jetpack
用户2018792831671 小时前
WMS(WindowManagerService的诞生
android
用户2018792831671 小时前
通俗易懂的讲解:Android窗口属性全解析
android
openinstall2 小时前
A/B测试如何借力openinstall实现用户价值深挖?
android·ios·html
二流小码农2 小时前
鸿蒙开发:资讯项目实战之项目初始化搭建
android·ios·harmonyos
志旭3 小时前
android15 vsync源码分析
android
志旭3 小时前
Android 14 HWUI 源码研究 View Canvas RenderThread ViewRootImpl skia
android
whysqwhw3 小时前
Egloo 高级用法
android