Android中的BroadcastReceiver

广播接收

在以下路径查询所有的 broadcast actions:<Android SDK 安装目录>/platforms/<Android API 版本>/data

分为动态注册和静态注册。动态注册将BroadcastReceiver写在Activity中,因此只有APP启动时才能接收广播。静态注册写在Manifest.xml中,APP未启动也能接收广播,但只能接收除隐式广播(没有具体指定发送给哪个应用程序的广播)之外的小部分广播。

动态注册

自定义一个继承BroadcastReceiver的类,重写父类的onReceive方法。然后实例化一个IntentFilter,添加要接收的action。最后用registerReceiver方法将自定义类和IntentFilter对象绑定。这样当收到对应action的广播时就会调用onReceive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MainActivity : AppCompatActivity() {
lateinit var timeChangeReceiver: TimeChangeReceiver

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intentFilter = IntentFilter()
intentFilter.addAction("android.intent.action.TIME_TICK")
timeChangeReceiver = TimeChangeReceiver()
registerReceiver(timeChangeReceiver, intentFilter)
}

override fun onDestroy() {
super.onDestroy()
unregisterReceiver(timeChangeReceiver)
}

inner class TimeChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "时间改变", Toast.LENGTH_SHORT).show()
}
}
}

注:不要在onReceive方法中添加过多的逻辑或者进行任何的耗时操作,因为BroadcastReceiver中是不允许开启线程的,当onReceive运行了较长时间而没有结束时,程序就会出现错误

静态注册

Android 8.0(API 级别 26)或更高级别为目标的应用无法在其清单中注册隐式广播的广播接收器,除非广播是专门发送给这些应用的。不过,有几种广播不受这些限制的约束。具体有哪些可以参考隐式广播例外情况

新建隐式BroadcastReceiver

以接收开机广播为例,在 Android Studio 中右键新建BroadcastReceiver,会将其自动添加到Manifest.xml。新建窗口中的Exported表示是否允许这个BroadcastReceiver接收本程序以外的广播,Enabled表示是否启用这个BroadcastReceiver。

Manifest.xml进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<manifest>

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

<application>

<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<!-- 若是自定义的广播,name为包名 -->
<!-- <action android:name="com.saoke.androiddemo.MY_BROADCAST"/> -->
</intent-filter>
</receiver>

</application>

</manifest>

广播发送

分为标准广播有序广播

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

标准广播

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

有序广播

发送标准广播

1
2
3
4
5
button.setOnClickListener {
val intent = Intent("com.saoke.androiddemo.MY_BROADCAST")
intent.setPackage(packageName) // packageName 即 getPackageName(),获取当前APP的包名
sendBroadcast(intent)
}

其中setPackage()指定这条广播是发送给哪个应用程序的,从而让它变成一条显式广播,否则静态注册的BroadcastReceiver将无法接收到这条广播

发送有序广播

1
2
3
4
5
button.setOnClickListener {
val intent = Intent("com.saoke.androiddemo.MY_BROADCAST")
intent.setPackage(packageName)
sendOrderedBroadcast(intent, "saoke.androiddemo")
}

其中sendOrderedBroadcast第二个参数为设置的权限,BroadcastReceiver要有对应权限才能接收到广播。权限为带.的字符串,无须权限可设为null。同样的标准广播也可设置权限。

可在Manifest.xml中设置BroadcastReceiver优先级,优先级高的先收到广播:

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
<manifest>

<!-- 若要接收的广播有设置权限,则APP要声明对应的权限 -->
<permission android:protectionLevel="normal" android:name="saoke.androiddemo" />

<application>

<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">

<!-- 设置优先级为 100 -->
<intent-filter android:priority="100">
<action android:name="com.saoke.androiddemo.MY_BROADCAST"/>
</intent-filter>

<!-- 若要接收的广播有设置权限,则接收器也要设置对应的权限 -->
<uses-permission android:name="saoke.androiddemo" />

</receiver>

</application>

</manifest>

可在onReceive中用abortBroadcast()截断广播,后面的BroadcastReceiver将无法再接收到这条广播:

1
2
3
4
5
6
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "MyBroadcastReceiver收到广播", Toast.LENGTH_SHORT).show()
abortBroadcast() // 截断广播
}
}