![Android移动应用开发技术与实践](https://wfqqreader-1252317822.image.myqcloud.com/cover/15/40681015/b_40681015.jpg)
2.4 BroadCastReceiver
Android系统的四大组件还包括BroadCastReceiver,这种组件就是一种全局的监听器,用于监听系统全局的广播消息。由于BroadCastReceiver是一种全局的监听器,因此它可以非常方便地实现系统中不同组件之间的通信。例如,我们希望客户端程序与startService()方法启动的Service之间通信,就可以借助于BroadCastReceiver来实现。
2.4.1 BroadCastReceiver简介
BroadCastReceiver用于接收程序(包括用户开发的程序和系统内建的程序)所发出的BroadCast Intent,与应用程序启动Activity、Service相同的是,程序启动BroadCastReceiver也只需要两步。
1)创建需要启动的BroadCastReceiver的Intent。
2)调用Context的sentBroadCast()或sendOrderedBroadCast()方法开启动指定的BroadCastReceiver。
使用BroadCastReceiver时需要注意以下几点:
● 当应用程序发出一个BroadCast Intent之后,所有匹配该Intent的BroadCastReceiver都有可能被启动。
● 与Activity、Service具有完整的生命周期不同,BroadCastReceiver本质上只是一个系统级的监听器——专门负责监听各程序所发出的BroadCast。实现BroadCastReceiver的方法十分简单,只要重写BroadCastReceiver的onReceive(Contextcontext, Intentintent)方法即可。
● 一旦实现了BroadCastReceiver,接下来就应该指定该BroadCastReceiver能匹配的Intent。
2.4.2 BroadCastReceiver生命周期
BroadcastReceiver的生命周期从对象调用它开始,到onReceiver方法执行完成后结束。每次广播被接收后会重新创建BroadcastReceiver对象,并在onReceiver方法中执行完就销毁,如果BroadcastReceiver的onReceiver方法中不能在10秒内执行完成,Android会出现ANR异常。所以不要在BroadcastReceiver的onReceiver方法中执行耗时的操作。
1)BroadCastReceiver的生命周期很短暂,当接收到广播的时候创建,当onReceive()方法结束后销毁。
2)正因为BroadCastReceiver的声明周期很短暂,所以不要在广播接收器中去创建子线程做耗时的操作,因为广播接收者被销毁后,这个子进程就会成为空进程,很容易被杀死。
3)因为BroadCastReceiver是运行在主线程的,所以不能直接在BroadCastReceiver中去做耗时的操作,否则就会出现ANR异常。
建议:耗时较长的工作最好放到Service中去完成。
2.4.3 BroadCastReceiver的类型
1.普通广播(Normal Broadcast)
普通广播对于多个接收者来说是完全异步的,通常每个接收者都无须等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。
发送广播通过context.sendBroadcast()方法,消息发送效率较高,但无法保证接收消息的先后顺序。
2.有序广播(Ordered Broadcast)
有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接收者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播。
发送广播通过context.sendOrderedBroadcast()方法,可以保证接收消息的先后顺序按照消息优先级高低来进行发送。
3.本地广播(Local Broadcast)
前两种广播都是全局广播,所有应用都可以接收到广播消息,这样存在一定的安全隐患,而本地广播只在进程内传播,可以有效地保证数据的安全。
发送广播可以通过LocalBroadcastManager来对广播进行管理,并提供了发送广播和注册广播的接收器的方法,主要代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/54_01.jpg?sign=1739286779-76yDUQveusMiKeXrtO4jALW24vh7HZ6y-0-f334810d3c2eb804d96854a755b78b74)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_01.jpg?sign=1739286779-FNiV7s0gqhYfLJA8BRp0fzyzfw4b5qTt-0-2972fda2f30bc0533c5b1d376301fd08)
4.系统广播(Local Broadcast)
Android内置了很多系统广播,当使用广播时,只需要在注册广播接收者时定义相关的Action即可,并不需要收到发送广播,当系统有相关操作(如开机、电池电量不足、拍照、网络变化等)时就会自动开启广播。
例如消息推送服务,需要实现开机启动的功能。要实现这个功能,就可以订阅系统“启动完成”这条广播,接收到这条广播后就可以启动自己的服务了。主要配置如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_02.jpg?sign=1739286779-QbJox6oiZjK11ytkT1UxQGGklslq47K6-0-39032de400c11d1c002ac3559a42b3c1)
同时从安全角度考虑,系统要求必须声明接收开机启动广播的权限,于是再声明使用下面的权限,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_03.jpg?sign=1739286779-s0a0kjkH8KOA6DCjTQTKtbcsnOCzZ5gZ-0-0ff82bb491d358299afbd87b456e6801)
2.4.4 BroadCastReceiver实现机制
广播接收器注册的方式分为两种:静态注册、动态注册。
1.静态注册
在AndroidManifest.xml里通过<receive>标签声明。
它的优点:不受其他组件生命周期影响,即使应用程序被关闭,也能接收广播;缺点:耗电,占内存;适用场景:需要时刻监听的广播。静态注册代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_04.jpg?sign=1739286779-8A4weV3DDo97vbZCKlIXm8CTXNPvRYK6-0-a4d9be920c77d96c1ec45b4c93872b8c)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_01.jpg?sign=1739286779-3KdZqkpyIUPky0P49enPR4M5stSNgWg9-0-15facc59f9a2c159ed58f3bd49e55713)
注册示例:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_02.jpg?sign=1739286779-0xAh3tpD82fYqyrg6sFhYnJVOZ5C7SD3-0-abc25b87b9cafad19dc7738f10179468)
当此App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。
2.动态注册
在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver。它的优点是:灵活,不耗电,易控,省内存;缺点:需要手动注销;适用场景:需要特定时候监听的广播。具体代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_03.jpg?sign=1739286779-6bVRvuGnTHHpKPqQn74ynHcMUvOczj3v-0-41897c6c9b4289f56749f79280bf02d8)
对于动态广播来说,有注册必然就有注销,需要成对出现。重复注册注销或者注册忘了注销都不行,后者会报Are you missing a call to unregisterReceiver()?错误,虽然不至于让应用崩溃,但是会导致内存泄露。
BroadCastReceiver注册好后,必须在接收到广播后才会被调用,因此,首先要发送广播。在Android中提供了两种发送广播的方式。
1)sendBroadCast():发送Normal Broadcast。
2)sendOrderedBroadCast():发送Ordered Broadcast。
普通广播(Normal Broadcast):Normal Broadcast是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高。但缺点是接收者不能将结果传递给下一个接收者,并且无法终止Broadcast Intent的广播。
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/57_01.jpg?sign=1739286779-BPw7LxhEVOs2MddNQ7GNS8nJdBfcXFl1-0-5d617d47e218be3042c4b7f9c90e52b9)
【例2-9】 布局文件
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/57_02.jpg?sign=1739286779-tWzozxZLw0DK5ex1MWde1aLwepmmqcG6-0-1f7e78683d33aa27064fdcf3201565ed)
对于设置条件,我们需要在AndroidManifest.xml中相应的目标下配置相同的条件,具体会在代码中说明。接下来附上主要代码。
新建项目,在MainActivity中添加下面代码,下面代码的主要功能是在主Activity中实现发送广播部分,主要代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/57_03.jpg?sign=1739286779-lmZdEc7Tke6DgxIk4wd3p3xMdt3DaMVr-0-31eddf7149a01123d38e6d96d21f9201)
新建一个类Receiver,用于接收发送出来的消息:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_01.jpg?sign=1739286779-ZpZcpZ60VROTBeUObzFhrw59ODJbtCpM-0-48d43717b77660e4e662aeb50b97c9b3)
最后还有最重要的一步,在AndroidManifest.xml配置Receiver类的广播接收意图:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_02.jpg?sign=1739286779-LtVjb64P0q5AVQsEyUqNwjPvvpT0Dm4j-0-177ae4cd28b195bcb5ea13804ee34f7f)
至此,对于普通广播的实现就完成了,运行后,在输出入日志中可以看到如下信息:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_03.jpg?sign=1739286779-ooB7QKdHU8W9YwLyrM9NWKx0hzGOMb7P-0-f25c5939e7d4feba0e4d3cfeab50add1)
有序广播(Ordered BroadCast):该广播的接收者将按预先声明的优先级(-1000~1000)依次接收广播。有序广播接收者可以终止广播的传播(通过调用abortBroadCast()方法),广播的传播一旦终止,后面的接收者就无法接收到广播。另外,广播接收者可以加入自己的数据传递给下一个接收者(通过setResultExtras(Bundle bundle)方法)。
优先级的设置:通常是在AndroidManifest.xml中注册广播地址时,通过android:priority属性设置广播接收的优先级。该属性值的范围是-1000~1000,数值越大,优先级越高。例如:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_04.jpg?sign=1739286779-py83bevOI90edworA9YPs25PciJS3uWe-0-17fd873d3598fd7f880084fda804420b)
发送有序广播:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_05.jpg?sign=1739286779-Q3iUFMq3NcTCzzaABS24CVeuKVkikxrX-0-b6486b72f39638a5ab54d584fe299a57)
发送有序广播的第二个参数是一个权限参数,如果为null则表示不要求BroadcastReceiver声明指定的权限。如果不为null,则表示接收者若想要接收此广播,需要声明指定的权限(为了安全)。
以上发送有序广播的代码需要在AndroidManifest.xml中自定义一个权限:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_06.jpg?sign=1739286779-YnRZrrX6Jz63lLISkoMrnzzdXQWvmN3w-0-1a6f9e98f98cc8e1d007749a20582e48)
然后声明使用了此权限:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_01.jpg?sign=1739286779-JKZRVEzAaA6oFutAdwQ2a0oVGsIbwG5e-0-f722c06d7a24f9794989d07e1fe05ee1)
终止广播需要在优先级高的BroadcastReceiver的onReceiver()方法中添加代码:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_02.jpg?sign=1739286779-nCMXOm8Fzdi5Olm5CuDWAkkVDRajWtLJ-0-d355711367e87db63b1e4099443b7f27)
则广播将不会再继续往下传播,即在低优先级的BroadcastReceiver中将不会再接收到广播消息。
【例2-10】 建立MainActivity
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_03.jpg?sign=1739286779-qmUMTavjv6nXQ7pSASI8qDjABUhpBqO9-0-f574e81d66f9a94be37d328e9c69f385)
代码中指定了Intent的Action属性,再调用sendOrderedBroadcast()方法来发送有序广播。对于有序广播,它会按优先级依次触发每个BroadcastReceiver的onReceiver()方法。
第一个BroadcastReceiver代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_04.jpg?sign=1739286779-4969qgtX9VohYsVhK8YMY2i7hS1ps2QN-0-03d230466261b7293c95ccaf14cd17e1)
MyReceiver不仅处理了它所接收的消息,而且向处理结果中存入了key为first的消息,这个消息将可以被第二个BroadcastReceiver解析出来。
abortBroadcast()用于取消广播,如果这条代码生效,那么优先级比MyReceiver低的BroadcastReceiver都将不会被触发。
在AndroidManifest.xml中部署该BroadcastReceiver,并指定其优先级为20,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_05.jpg?sign=1739286779-oxYHuWUGEniY2x6An1s4PQEpJk6dH1Xz-0-5ebf07921340140da4d340c2fc511fd2)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_01.jpg?sign=1739286779-NlfQU3iYxmW6sM2FroXdSewQMvBg3st3-0-8a6447d68b5b35d5e181ef2355164634)
接下来提供第二个BroadcastReceiver,将会解析前一个BroadcastReceiver存入的key为first的消息,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_02.jpg?sign=1739286779-HvgnyP2ZmukmIqX9xpAQe87InbNXLp8d-0-eaa15798b30e8aef6b06357fd52ff550)
解析出前一个BroadcastReceiver存入结果中的key为first的消息。
在AndroidManifest.xml中配置MyReceiver2的优先级为0,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_03.jpg?sign=1739286779-gQaqdL7TUhdvRdCNsBy9VoetKd3OwghQ-0-d6db969aed4b3891af9c9446ddee956f)
先注释掉abortBroadcast(),点击发送有序广播按钮,可以看到先显示第一个广播接收器中的内容,再显示第二个广播接收器中的内容。
运行程序,其结果如图2-13~图2-15所示。
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_04.jpg?sign=1739286779-CskLnb3NBoKiKFMZnl5XrV1ZjihIfTPf-0-456fe6894a2d4c4e3ac94cd3fd559513)
图2-13 运行首页
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_05.jpg?sign=1739286779-mxVxn8GEneNSeFe6JAqYYSmpT9iStK9z-0-5d22dee67ffd1e7a1622d79884ef2372)
图2-14 消息存入
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_06.jpg?sign=1739286779-HXRmPI00JzcRKX49yHilbxSYHVuGO51T-0-d7b11c49f263ce871bd84d119d796264)
图2-15 消息接收
根据上面的配置可以看出,该程序中包含两个Broadcast Receiver,其中MyReceiver的优先级更高,MyReceiver2的优先级略低。