发送自定义广播

前言

前面我们说过,广播(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 会先收到广播消息。

注意:相同优先级的接收器,接收广播的顺序是不确定的。

然后在 MyBroadcastReceiveronReceive() 方法中调用 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" 的提示信息。

相关推荐
duwei_wang4 小时前
[Android]-Admob配置过多导致的慢消息
android
雨白5 小时前
深入理解广播机制 (BroadcastReceiver)
android
婵鸣空啼9 小时前
GD图像处理与SESSiON
android
sunly_10 小时前
Flutter:导航固定背景图,滚动时导航颜色渐变
android·javascript·flutter
用户20187928316711 小时前
简单了解android.permission.MEDIA_CONTENT_CONTROL权限
android
_一条咸鱼_11 小时前
Android Runtime类卸载条件与资源回收策略(29)
android·面试·android jetpack
顾林海11 小时前
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
android·面试·性能优化
砖厂小工11 小时前
Now In Android 精讲 8 - Gradle build-logic 现代构建逻辑组织方式
android
玲小珑11 小时前
Auto.js 入门指南(八)高级控件与 UI 自动化
android·前端