发送自定义广播

前言

前面我们说过,广播(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" 的提示信息。

相关推荐
踢球的打工仔3 小时前
PHP面向对象(7)
android·开发语言·php
安卓理事人3 小时前
安卓socket
android
安卓理事人9 小时前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学10 小时前
Android M3U8视频播放器
android·音视频
q***577410 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober11 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿12 小时前
关于ObjectAnimator
android
zhangphil12 小时前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我13 小时前
从头写一个自己的app
android·前端·flutter
lichong95115 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端