前言
前面我们说过,广播(Broadcast)是一种消息传递机制,它可用于应用之间以及应用与系统之间进行通信,我们现在就来看看如何发送自定义广播。
发送标准广播
标准广播是"异步"的广播,所有相关的接收器会在同一时间、无序地接收这条广播。
在发送广播之前,我们先创建一个广播接收器来接收将要发送的广播。创建 MyBroadcastReceiver
类,继承自 BroadcastReceiver
,并重写 onReceive()
方法:
kotlin
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(
context, "received in MyBroadcastReceiver",
Toast.LENGTH_SHORT
).show()
}
}
当收到我们自定义的广播消息后,就会弹出 Toast 提示。
然后在 AndroidManifest.xml
清单文件中静态注册这个广播接收器,并通过 <intent-filter>
标签指定其"订阅"的广播 action
:
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
...>
...
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
</application>
</manifest>
其中 android:exported="true" 表示允许接收来自其他应用的广播,忽略这个属性,会报错:As of Android 12, android:exported must be set; use true to make the activity available to other apps, and false otherwise.
接下来修改 activity_main.xml
布局文件,在其中添加一个按钮,用于触发广播的发送。
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send Broadcast" />
</LinearLayout>
最后在 MainActivity
发送自定义广播:
kotlin
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener {
val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
intent.setPackage(packageName)
sendBroadcast(intent)
}
}
}
可以看到我们在按钮的点击回调中,发送了一条自定义广播。
首先,我们创建了一个 Intent 对象,并把 action 值传入,然后调用了 Intent 的 setPackage()
方法,设置了广播的包名,指定当前广播是发给哪个应用程序的,让它变为显式广播。否则默认情况下,自定义广播都是隐式广播,在 Android 8 + 系统上,通过静态方式注册的 BroadcastReceiver 无法接收到这条广播。最后我们调用 sendBroadcast()
方法将广播发送出去了。
这样所有订阅了 action 值为 com.example.broadcasttest.MY_BROADCAST
广播,就会接收到广播消息。
运行程序后,点击按钮,效果如下:
发送有序广播
和标准广播不同的是,有序广播是一种"同步"广播,系统会按照广播接收器的优先级,来传递广播。高优先级的接收器会先收到广播,我们可以对广播进行处理,或者选择截断广播,阻止其继续传递。
我们新建一个广播接收器 AnotherBroadcastReceiver
,代码如下:
kotlin
class AnotherBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(
context, "received in AnotherBroadcastReceiver",
Toast.LENGTH_SHORT
).show()
}
}
也要在清单文件中进行注册,并且给它指定要接收 com.example.broadcasttest.MY_BROADCAST
这条广播。
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
...>
...
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
<receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
</application>
</manifest>
然后回到 MainActivity
中,发送有序广播:
kotlin
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener {
val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
intent.setPackage(packageName)
sendOrderedBroadcast(intent, null)
}
}
}
可以看到,与发送标准广播相比,只是将发送广播的方法改为了 sendOrderedBroadcast()
,第一个参数还是 Intent 对象,第二个参数是与权限相关的字符串,这里简单填写 null。
现在重新运行程序,效果如下:

会弹出两条提示,好像和标准广播并没有什么区别。这是因为我们没有设置优先级,并且没在 onReceive()
方法中对广播进行任何的处理。
我们先来修改 BroadcastReceiver 的优先级,来到 AndroidManifest.xml
文件中:
xml
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
<receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>
在 <intent-filter>
标签中,通过 android:priority
属性将 MyBroadcastReceiver 的优先级设为了 100,将 AnotherBroadcastReceiver 的优先级设为了 1,这样 MyBroadcastReceiver 会先收到广播消息。
注意:相同优先级的接收器,接收广播的顺序是不确定的。
然后在 MyBroadcastReceiver
的 onReceive()
方法中调用 abortBroadcast()
方法将广播进行截断:
kotlin
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(
context, "received in MyBroadcastReceiver",
Toast.LENGTH_SHORT
).show()
// 截断广播
abortBroadcast()
}
}
这样 AnotherBroadcastReceiver
就无法接收到广播了。
运行效果:
可以看到只有 "received in MyBroadcastReceiver"
的提示信息。