Android中的BroadcastReceiver

目录

注:本文中代码部分使用Kotlin语言进行编写

一、介绍

广播分为两个方面: 广播发送者和广播接收者(BroadcastReceiver)
广播类型:
1. 标准广播(normal broadcasts): 是一种完全异步执行的广播,在广播发出去之后,所有的BroadcastReceiver几乎会在同一时刻收到这条广播。所以对于BroadcastReceiver来说它们没有顺序可言。这种广播效率高,但也无法被截断。
2. 有序广播(ordered broadcasts): 同步执行广播,在广播发出去之后同一时刻只会有一个BroadcastReceiver接收到广播,当这个BroadcastReceiver中的逻辑执行完之后才会继续传递。所以,对于BroadcastReceiver来说是有先后顺序之分的,优先级高的BroadcastReceiver先接受到广播,优先级低的BroadcastReceiver后接收到广播,并且BroadcastReceiver还可以截断广播,使优先级低于当前BroadcastReceiver的BroadcastReceiver无法接收到广播。
使用场景:
同一app内部的同一组件内的消息通信(单个或多个线程之间)
同一app内部的不同组件之间的消息通信(单个进程)
同一app具有多个进程的不同组件之间的消息通信
不同app之间的组件之间消息通信Android系统在特定情况下与App之间的消息通信。

二、简单使用

BroadcastReceiver在使用时要先进行注册,注册BroadcastReceiver的方式一般分为静态注册(在AndroidManifest.xml中注册)、动态注册(在代码中注册)。静态注册可以让程序在未启动的状态下也能接收广播,而动态注册则只能在程序启动之后才能接收广播。

2.1 静态广播

以接收开机广播为例

新建BroadcastReceiver,右键点击含有主类的包,New->Other->Broadcast Receiver,
Android中的BroadcastReceiver
在新建的BroadcastReceiver中编写逻辑

class MyReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        
        Toast.makeText(context,"成功接收开机广播",Toast.LENGTH_LONG).show()
        
    }
}

注:BroadcastReceiver中是不允许开启线程的,当onReceive()方法运行较长时间时,程序会报错,不建议添加过多的逻辑及耗时间的操作

AndroidManifest.xml中注册,其中enabled属性表示表示是否启用这个BroadcastReceiver,exported属性表示是否允许BroadcastReceiver接收本程序外的广播

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

receiver标签中添加一个intent-filter标签,使BroadcastReceiver可以接受开机广播

 <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

为了保证用户隐私,接收开机广播需要使用系统权限,在AndroidManifest.xml中声明系统权限(在application上方声明)

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

2.2 自定义广播

2.2.1 标准广播

创建一个BroadcastReceiver来接收广播,参考上述静态广播中的创建方法

class MyReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        Toast.makeText(context,"成功接收开机广播",Toast.LENGTH_LONG).show()

    }
}

receiver标签中添加一个intent-filter标签,使BroadcastReceiver可以接受自定义广播广播

 <intent-filter>
                <action android:name="com.example.broadcastreceviertext.MY_BROADCAST"/>
            </intent-filter>

activity_main.xml中添加代码,创建一个按钮作为触发点

<Button
		android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="BUTTON"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

给按钮设置点击监听事件

button.setOnClickListener { 
			//新建一个Intent对象,并将要发送的广播的值传入
            val intent = Intent("com.example.broadcastreceviertext.MY_BROADCAST")
            //将当前应用程序的包名传给intent
            intent.setPackage(packageName)
            //发送广播
            sendBroadcast(intent)		
        }

注:Android8.0系统之后,静态注册的BroadcastReceiver是无法接受隐式广播的,默认情况下我们创建的自定义广播均为隐式广播,所以要调用setPackage()方法指定广播要发给那个应用程序,从而将广播设置为显式广播

运行程序,点击按钮即可发送广播,在BroadcastReceiver接受到广播时将会执行我们编写的逻辑Toast.makeText(context,"成功接收开机广播",Toast.LENGTH_LONG).show()

由于广播是使用Intent来发送的,所以我们还可以在发送广播的同时携带数据传递给相应的BroadcastReceiver

2.2.2 有序广播

有序广播式一种同步执行广播,并且可以被截断
再创建一个BroadcastReceiver,新建MyReceiver_2

class MyReceiver_2 : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"MyReceiver_2成功接收开机广播", Toast.LENGTH_LONG).show()
    }
}

修改AndroidManifest.xml中代码,使两个BroadcastReceiver监听同一条广播

<receiver
            android:name=".MyReceiver_2"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.broadcastreceviertext.MY_BROADCAST" />
            </intent-filter>
        </receiver>

**运行程序可以看到分别弹出两条提示信息

成功接收开机广播
MyReceiver_2成功接收开机广播

修改按钮监听事件的代码

button.setOnClickListener { 
			//新建一个Intent对象,并将要发送的广播的值传入
            val intent = Intent("com.example.broadcastreceviertext.MY_BROADCAST")
            //将当前应用程序的包名传给intent
            intent.setPackage(packageName)
            //发送有序广播
             sendOrderedBroadcast(intent,null)		
        }

添加代码android:priority="100",将MyReceiver的优先级设置为100,保证MyReceiver的优先级高于MyReceiver_2

<receiver
            android:name=".MyReceiver_2"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.broadcastreceviertext.MY_BROADCAST" />
            </intent-filter>
        </receiver>
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter  android:priority="100">
                <action android:name="com.example.broadcastreceviertext.MY_BROADCAST" />
            </intent-filter>
        </receiver>

修改 MyReceiver中的代码,将广播截断

class MyReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        Toast.makeText(context,"MyReceiver成功接收开机广播",Toast.LENGTH_LONG).show()
		//将广播截断
		abortBroadcast()
    }
}

运行程序可以看到只会弹出MyReceiver中设置的提示信息

成功接收开机广播

使用sendOrderedBroadcast()方法,将广播设置为有序广播,并且MyReceiver的优先级比MyReceiver_2高,所以MyReceiver先接收到广播,MyReceiver_2后接收广播,但是MyReceiver中使用abortBroadcast()方法将广播截断,所以MyReceiver_2无法接收到广播

上一篇:【Android】BroadcastReceiver


下一篇:Android系统编程入门系列之广播接收者BroadcastReceiver实现进程间通信