广播机制简介

 Android中的广播机制十分灵活,每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于其他应用程序的。

 Android提供了一套完整的API,允许应用程序自由地发送和接收广播。

 发送广播可以借助Intent,接收广播则需要借助BroadcastReceiver

 广播的类型

 标准广播和有序广播。

 标准广播:一种完全异步执行的广播,发出后,所有的BroadcastReceiver几乎同时收到这条广播消息,没有先后顺序。效率高,但无法被截断。

 有序广播:一种异步执行的广播,发出后,同一时刻只能有一个BroadcastReceiver收到消息,当这个BroadcastReceiver处理完后,广播才能继续传递下去,有先后顺序。优先级高的BroadcastReceiver会先收到广播消息。并且还可以截断正在传递的广播。

 接收系统广播

 Android内置了很多系统级别的广播,比如手机开机,电量变化等等。想要接收他们的广播,就要用到BroadcastReceiver。

 动态注册监听时间变化

 注册方法一般有两种:在代码中注册(动态注册)和在AndroidManifest.xml中注册(静态注册)。

 那么如何创建一个BroadcastReceiver呢?

 只需要新建一个类,继承自BroadcastReceiver,并重写父类的onReceive()方法即可。当有广播来时,onReceive()方法就会执行。

 例子:动态注册一个监听时间变化的程序。

 在MainActivity中定义了一个内部类TimeChangeReceiver,继承自BroadcastReceiver,重写了onReceive()方法。

inner class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(p0: Context?, p1: Intent?) {
            TODO("Not yet implemented")
        }
    }

 

 在onCreate()方法,先创建一个IntentFilter的实例,给它添加一个值为android.intent.action.TIME_TICK的action。因为系统时间发生变化时,系统发出的正是一条值为android.intent.action.TIME_TICK的广播。也就是说,想要监听什么广播,就在这里添加相应的action。

 接下来创建一个TimeChangeReceiver的实例,然后调用registerReceiver()方法进行注册,将TimeChangeReceiver的实例和IntentFilter的实例都传了进去,这样TimeChangeReceiver就会收到所有值为android.intent.action.TIME_TICK的广播,也就实现了监听系统时间变化的功能。

val intentFilter = IntentFilter()
        intentFilter.addAction("android.intent.action.TIME_TICK")
        timeChangeReceiver = TimeChangeReceiver()
        registerReceiver(timeChangeReceiver,intentFilter)

 

 这里需要将timeChangeReceiver声明为全局变量,需要用到延迟初始化:

lateinit var  timeChangeReceiver: TimeChangeReceiver

 

 最后记得,动态注册的BroadcastReceiver一定要取消注册才行,在onDestory()中调用unregisterReceiver()方法即可。

unregisterReceiver(timeChangeReceiver)

 

 最后运行程序,系统每隔一分钟就会发出一条android.intent.action.TIME_TICK的广播。

 

 想要查看完整的系统广播列表,前往如下路径:

<Android SDK>/platforms/<任意android api版本>/data/broadcast_actions.txt

 

 静态注册实现开机启动

 动态注册的可以自由地控制注册与注销,在灵活性方面有很大的优势。但是有一个缺点就是必须在程序启动之后才能接收广播。因为实在onCreate()中注册的。

 静态注册则能实现程序未启动的情况下也能接收广播。(简述一下原理,在安装应用的时候,系统会启动PackageManagerService管理服务,这个服务对AndroidManifest进行解析,从而得到应用程序的相关信息,比如service,activity,Broadcast等等。)

 实现开机广播的action值为android.intent.action.BOOT_COMPLETED。

 首先我们需要新建一个广播类,可以通过New->Other->Broadcast新建。新建时有两个属性:Exported属性表示是否允许这个BroadcastReceiver接收本程序以外的广播,Enabled属性表示是否启用这个BroadcastReceiver。都勾选上。

 然后在onReceive()中编写逻辑即可。

 另外,静态的BroadcastReceiver需要在AndroidManifest.xml文件中注册。不过新建时自动注册了。

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

 

 同时,需要在<receiver>标签中添加了一个<intent-filter>标签,在里面声明相应的action。

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

最后再进行权限声明。

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

 

 发送自定义广播

 发送标准广播

 1. 创建广播接收者:在发送广播之前,需要先定义一个BroadcastReceiver来准备接收此广播。新建一个MyBroadcastReceiver,并在onReceive()方法中编写逻辑。

 2. 添加广播接收者需要接收的action:在AndroidManifest对这个BroadcastReceiver修改。将action设为com.example.broadcasttest.MY_BROADCAST。所以等会就会发送这样一条广播。

 3. 发送广播:在MainActivity中的添加按钮点击事件,里面编写广播发送代码。

val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
        intent.setPackage(packageName)
        sendBroadcast(intent)

 

 为什么会用到setPackage()方法:在Android 8.0系统之后,静态注册的BroadcastReceiver是无法接收隐式广播的,而默认情况下我们发出的自定义广播恰恰都是隐式广播。因此这里一定要调用setPackage()方法,指定这条广播是发送给哪个应用程序的,从而让它变成一条显式广播,否则静态注册的BroadcastReceiver将无法接收到这条广播。

 

 发送有序广播

 有序广播是可以被截断的。发送有序广播只需要改动一行代码。

sendOrderedBroadcast(intent,null)

 

 此方法接收两个参数:第一个Intent,第二个是一个与权限相关的字符串,这里传入null就行了。

 这个时候的BroadcastReceiver是有先后顺序的,而且前面的BroadcastReceiver还可以将广播截断,以阻止其继续传播。

 设定先后顺序是在注册的时候进行设定,修改AndroidManifest.xml中的代码,如下所示:

<intent-filter android:priority="1">
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>

 

 通过android:priority属性设置优先级,值越大优先级越高。

 然后在onReceive()方法中调用abortBroadcast()方法,表示将这条广播截断,后面的BroadcastReceiver将无法再接收到这条广播。

override fun onReceive(context: Context, intent: Intent) {
        TODO("BootCompleteReceiver.onReceive() is not implemented")
        abortBroadcast()
    }