抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

什么是 BroadcastReceiver

在 Android 中,BroadcastReceiver 是一种用于接收和处理广播事件的组件。广播是一种 Android 应用之间传递消息的方式,适合在应用之间共享事件,或在系统状态发生变化时通知应用。例如,当设备启动完成、连接到 Wi-Fi、接收到短信等,系统会发送相应的广播消息,而应用可以通过 BroadcastReceiver 来监听这些事件。它的工作原理是基于发布-订阅模式。应用程序可以选择注册感兴趣的广播消息,并在消息到达时进行响应。这种机制使得不同的应用程序之间能够实现通信和协作,同时也提供了一种系统级别的事件通知机制。

标准广播和有序广播

标准广播 (Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

有序广播 (Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。

静态注册和动态注册

不同于其他三大组件,广播接收器既可以在 AndroidManifest.xml 中进行静态注册,也可以在代码中进行动态注册。

  1. 静态注册

    静态注册 是在 AndroidManifest.xml 文件中声明 BroadcastReceiver,并定义其要监听的广播类型。这种方式适合用于监听 系统广播 和一些全局广播事件。

    静态注册的广播接收器与设备共存亡,可以让应用在未运行时也能接收到广播消息。适合需要在应用未启动时也能接收到的广播事件,比如设备启动完成、安装应用等。

    在设备重启后,静态注册的广播可以继续监听指定事件,而不需要重新注册。

    实现方法

    AndroidManifest.xml 中声明一个 <receiver> 元素,并定义广播接收器及其要监听的广播事件。

    静态注册的限制

    Android 8.0(API 级别 26) 开始,系统对静态广播的注册有所限制,某些广播事件只能使用 动态注册 监听,例如 CONNECTIVITY_ACTION(网络状态变化)。这些事件只能在应用运行时注册和接收,避免消耗设备资源。

  2. 动态注册

    动态注册 是通过在代码中使用 registerReceiver() 方法注册 BroadcastReceiver。通常在 ActivityService 等组件的生命周期内注册和注销广播接收器。并在适当的时候调用 unregisterReceiver() 方法进行解注册。

    动态注册的广播接收器与注册它的 Activity 共存亡,只有当应用处于运行状态时,接收器才会接收到广播消息。适合在组件的特定生命周期内监听广播事件,例如在 Activity 显示期间监听网络变化,停止后不再监听。对于 Android 8.0 以上版本受限制的广播事件,必须使用动态注册。

    实现方法

    在代码中创建并注册 BroadcastReceiver,当不需要监听时,通过 unregisterReceiver() 取消注册。

静态注册和动态注册的对比

特性 静态注册 动态注册
注册方式 AndroidManifest.xml 中声明 在代码中使用 registerReceiver()
生命周期 生命周期与应用一致,可在应用未启动时接收广播 生命周期与组件(如 Activity)一致,需在适当时取消
使用场景 适合全局广播,特别是系统启动广播等 适合在应用运行期间临时监听的广播,例如网络状态变化
受 Android 8.0+ 限制 部分广播受限制,需改用动态注册 不受限制,可以正常使用
内存消耗 长时间监听,可能消耗资源 生命周期内监听,合理控制内存消耗

BroadcastReceiver 的基本使用

静态注册接收开机广播

首先创建一个 BootCompleteReceiver 类,重写 onReceive() 方法,使用 Toast 弹出一段提示信息。

1
2
3
4
5
6
7
8
public class BootCompleteReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
}

}

因为是静态注册,所以需要在 AndroidManifest.xml 文件中进行注册

1
2
3
4
5
<receiver android:name=".BootCompleteReceiver">
<intent-filter>
<action android:name = "android.intent.cation.BOOT_COMPLETED">
</intent-filter>
</receiver>

由于 Android 系统启动完成后会发出一条值为 android.intent.action.BOOT_COMPLETED 的广播,因此我们在 <intent-filter> 标签里添加了相应的 action。

除此之外,还要给应用添加权限。

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

主活动中不需要添加任何代码。

动态注册监听网络状态变化

以监听网络变化为例,讲解如何动态注册广播接收者。

首先我们需要创建一个 BroadcastReceiver 类,重写父类的 onReceive() 方法来实现网络变化后动作。

1
2
3
4
5
6
public class NetworkReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
}
}

然后再主活动中动态注册该广播接收者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainActivity extends AppCompatActivity {

private NetworkReceiver networkReceiver;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化广播接收者
networkReceiver = new NetworkReceiver();
//添加意图为网络状态改变
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//使用 registerReceiver()动态注册广播接收者
registerReceiver(networkReceiver, intentFilter);
}

@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkReceiver);
}
}

当网络状态发生变化时,系统会发出一条值为 android.net.conn.CONNECTIVITY_CHANGE 的广播,因为我们的广播接收器需要监听这个广播,所以 action 就设置此值。

最后,不要忘记将广播接收器取消注册。动态注册的广播接收器与活动共存亡,所以重写活动的 onDestroy() 方法,调用 unregisterReceiver() 方法来实现。

发送自定义广播

发送标准广播

在发送广播前,我们仍然要定义一个广播接收器来接收我们发送的广播。

1
2
3
4
5
6
7
8
9
public class MyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_
SHORT).show();
}

}

AndroidManifest.xml 中进行注册。

1
2
3
4
5
6
7
8
<receiver android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<!--自定义一个动作-->
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>

接下来修改 activity_main.xml 中的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send Broadcast"
/>

</LinearLayout>

MainActivity.java 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity extends AppCompatActivity {

private NetworkReceiver networkReceiver;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void sendBroadcast(View view) {
Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
//SDK 版本 > 26 需要添加
//intent.addFlags(0x01000000);
sendBroadcast(intent);
}
}

Android 8.0(API 级别 26)起,Android 引入了后台执行限制,以提高设备性能和安全性。这意味着在后台运行的应用程序将无法接收到大多数隐式广播,除非应用程序处于前台或具有前台服务。

发送有序广播

另外创建一个项目,同样接收 com.example.broadcasttest.MY_BROADCAST 的广播消息,同样弹窗显示:

1
2
3
4
5
6
7
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in AnotherBroadcastReceiver",
Toast.LENGTH_SHORT).show();
}
}

AndroidManifest.xml 中注册:

1
2
3
4
5
6
7
8
<receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>

然后将其安装到测试机上。

发送有序广播的代码在上 2.3.1 的基础上,只需要将 sendBroadcast(intnet) 方法改为 sendOrderedBroadcast(intent,receiverPermission ) 方法即可。其中参数 receiverPermission 是一个与权限有关的字符串,这里传入 null 就行了。

广播接收器的优先级

既然有序广播在接收时是有先后顺序的,那么就我们可以设置广播接收器的先后顺序。在 AndroidManifest.xml<intent-filter> 中,使用属性 android:priority 来设置优先级,值越大优先级越高,优先级越高的广播接收器就越先接收到广播。

截断广播传递

既然已经获得了接收广播的优先权,那么我们就可以对这条广播进行截断了。操作起来比较容易,只需要在自定义的 BroadcastReceiver 类的 onReceive() 方法中添加 abortBroadcast() 方法即可,相关代码如下:

1
2
3
4
5
6
7
8
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("MyBroadcastReceiver", "Received");
Toast.makeText(context, "MyBroadcastReceiver received", Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}

使用本地广播

在前面的代码中,我们发送和接收的广播全部属于系统全局广播,即发出的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。

但这样就很容易引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。因此 Android 引入了一套本地广播机制来解决这些安全性问题,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播。

本地广播的用法并不复杂,主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。

在使用前,我们需要注意一些事项:

  1. 本地广播无法通过静态注册来接收。因为发送本地广播时,程序肯定已经启动了,没有必要用静态注册。况且静态注册的话,会接收到来自非自生应用程序的广播。
  2. 在广播中启动 Activity 的话,需要 Intent 中添加 FLAG_ACTIVITY_NEW_TASK 的 Flag,不然会报错,因为需要一个栈来存放新打开的 Activity。
  3. 如果通过广播来弹出 AlertDialog,需要设置对话框的类型为 TYPE_SYSTEM_ALERT

自定义 BroadcastReceiver 类,代码:

1
2
3
4
5
6
7
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("MyBroadcastReceiver", "Received");
Toast.makeText(context, "MyBroadcastReceiver received", Toast.LENGTH_SHORT).show();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MainActivity extends AppCompatActivity {

private static final String ACTION_MY_BROADCAST = "com.example.broadcasttest.MY_BROADCAST";
private LocalBroadcastManager localBroadcastManager;
private MyBroadcastReceiver MyBroadcastReceiver;


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
//注册本地广播接收器
IntentFilter intentFilter = new IntentFilter(ACTION_MY_BROADCAST);
MyBroadcastReceiver = new MyBroadcastReceiver();
localBroadcastManager.registerReceiver(MyBroadcastReceiver, intentFilter);

}

public void sendBroadcast(View view) {
Intent intent = new Intent(ACTION_MY_BROADCAST);
//intent.addFlags(0x01000000);
//发送本地广播
localBroadcastManager.sendBroadcast(intent);
}

@Override
protected void onDestroy() {
super.onDestroy();
//记得要取消注册
localBroadcastManager.unregisterReceiver(MyBroadcastReceiver);
}
}

跟动态注册的代码没有很大的区别,只不过是通过 localBroadcastManager 来注册和销毁广播接收器

评论