![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=1738838222-9WoJdXcMLaKExW6EU2JjBAVbb5MneEQD-0-b846d40fcaa116f5a81bdf6d914fd4e3)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_01.jpg?sign=1738838222-A53tHiy6AhMj5UJcsjrh5oP9TmjvYoUU-0-d03cbae4cbe0b013fa2208fc18fe1c5b)
4.系统广播(Local Broadcast)
Android内置了很多系统广播,当使用广播时,只需要在注册广播接收者时定义相关的Action即可,并不需要收到发送广播,当系统有相关操作(如开机、电池电量不足、拍照、网络变化等)时就会自动开启广播。
例如消息推送服务,需要实现开机启动的功能。要实现这个功能,就可以订阅系统“启动完成”这条广播,接收到这条广播后就可以启动自己的服务了。主要配置如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_02.jpg?sign=1738838222-qB7dksAqwKWGd8K5iXlyrevo1aHb3gZs-0-010759d8bff87d4f12a4e470b2ebee62)
同时从安全角度考虑,系统要求必须声明接收开机启动广播的权限,于是再声明使用下面的权限,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_03.jpg?sign=1738838222-dFJLes4CcoCtptY61eLFnApl9q3O5P2a-0-b499f6c68041103e18580a19f740effd)
2.4.4 BroadCastReceiver实现机制
广播接收器注册的方式分为两种:静态注册、动态注册。
1.静态注册
在AndroidManifest.xml里通过<receive>标签声明。
它的优点:不受其他组件生命周期影响,即使应用程序被关闭,也能接收广播;缺点:耗电,占内存;适用场景:需要时刻监听的广播。静态注册代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_04.jpg?sign=1738838222-tr2AAH7lAC6iV9PEyhA0Z0da6e0U9scB-0-cbb19f28ae652e86c37040e17dce5b4e)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_01.jpg?sign=1738838222-WTCbBJPYNkWo6me3V5KmPzg7nMGW9NGQ-0-1b22b3fa30dd07930c3bf59e9d76a3a6)
注册示例:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_02.jpg?sign=1738838222-71pNSnpmftFEeNtx9oz1VtECnXrwSJ4T-0-1170f67c495e06fcbdd5a07251c2ff84)
当此App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。
2.动态注册
在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver。它的优点是:灵活,不耗电,易控,省内存;缺点:需要手动注销;适用场景:需要特定时候监听的广播。具体代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_03.jpg?sign=1738838222-PNToW2PuC3LpDIp8kPxGACfIqpCf6Fbn-0-a7a0559816274b81b791fe8b4fc1c211)
对于动态广播来说,有注册必然就有注销,需要成对出现。重复注册注销或者注册忘了注销都不行,后者会报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=1738838222-nilShpRhqAzgnPDc48k19UfSirkP2Hjk-0-63798bf22e131f455756d6d7316ee65b)
【例2-9】 布局文件
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/57_02.jpg?sign=1738838222-zLim2a9W2pa4V0f5D7xAz1FKKB1zRr0m-0-64fd48fb82981ff68478ceede089c92d)
对于设置条件,我们需要在AndroidManifest.xml中相应的目标下配置相同的条件,具体会在代码中说明。接下来附上主要代码。
新建项目,在MainActivity中添加下面代码,下面代码的主要功能是在主Activity中实现发送广播部分,主要代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/57_03.jpg?sign=1738838222-RyCvxVHRYngmOJl3d3e1HfVPVGqcgKk6-0-ffd2b2eb54f43bf32031391caae45af6)
新建一个类Receiver,用于接收发送出来的消息:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_01.jpg?sign=1738838222-PQxPMJqefbdOXfc7Ba8EdD1UbFNwR96C-0-50a6601a244f8c2fd7d0975e4b0e1e4c)
最后还有最重要的一步,在AndroidManifest.xml配置Receiver类的广播接收意图:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_02.jpg?sign=1738838222-N2SjE9GIMYrHDgAz0yevA6JBXe40pPwl-0-e7c2895decfa9e0e22d57f857fc93396)
至此,对于普通广播的实现就完成了,运行后,在输出入日志中可以看到如下信息:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_03.jpg?sign=1738838222-RvuoD88Mb3XiWHxIifte1zFWEHJTZhFX-0-fd6fe3fa015fb0e41b7fd6703776e224)
有序广播(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=1738838222-oKNULWq2bKkB19dsopYaNpI7xuR2dGe8-0-60d56e521312126bcf1d3ab849e4e674)
发送有序广播:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_05.jpg?sign=1738838222-1iGp1ccOh6uRrzsaAq8ZCytLQPMuOu6r-0-f6d80c76a34e0b7ea76a30cbfc50978b)
发送有序广播的第二个参数是一个权限参数,如果为null则表示不要求BroadcastReceiver声明指定的权限。如果不为null,则表示接收者若想要接收此广播,需要声明指定的权限(为了安全)。
以上发送有序广播的代码需要在AndroidManifest.xml中自定义一个权限:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_06.jpg?sign=1738838222-DUZSnBrGyP0TPi8uEvEuz1gxlxxJLbP8-0-307716310fb8eb2cd5c62bbba3fe8bda)
然后声明使用了此权限:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_01.jpg?sign=1738838222-Klvztg52IdShLXyBhxyUiW1AJ9MPyidp-0-c57743777ceca7be7af2019304b1bd6f)
终止广播需要在优先级高的BroadcastReceiver的onReceiver()方法中添加代码:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_02.jpg?sign=1738838222-ENN5qOe9CoRTvFlpj3K2xXgtOuh6WdRX-0-5c94f0d86dbaeb20b2aedec8f02e4ae4)
则广播将不会再继续往下传播,即在低优先级的BroadcastReceiver中将不会再接收到广播消息。
【例2-10】 建立MainActivity
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_03.jpg?sign=1738838222-D0RJlxHGC5pifELZHArntZ0dav1cDdmT-0-7f6a08b065f9742e5df326361a8875b1)
代码中指定了Intent的Action属性,再调用sendOrderedBroadcast()方法来发送有序广播。对于有序广播,它会按优先级依次触发每个BroadcastReceiver的onReceiver()方法。
第一个BroadcastReceiver代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_04.jpg?sign=1738838222-LcOUCAyytVOFAeW3hoGoroI47TtDxjdT-0-eb124c7dc4741b95479753cd61c31ab8)
MyReceiver不仅处理了它所接收的消息,而且向处理结果中存入了key为first的消息,这个消息将可以被第二个BroadcastReceiver解析出来。
abortBroadcast()用于取消广播,如果这条代码生效,那么优先级比MyReceiver低的BroadcastReceiver都将不会被触发。
在AndroidManifest.xml中部署该BroadcastReceiver,并指定其优先级为20,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_05.jpg?sign=1738838222-bp78FBo3ktoooya4Wq3em3IYY3HQUdOV-0-bd2c75d473b2fe86a561031cbfb79d85)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_01.jpg?sign=1738838222-ZAAv5E94OwCgxN3OJoIYF1caZ5mOEgpI-0-efdf71151a1fd52aaa5b4914a4980b8f)
接下来提供第二个BroadcastReceiver,将会解析前一个BroadcastReceiver存入的key为first的消息,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_02.jpg?sign=1738838222-ZvrkKZJwDq3nzAH6b42fSi4hCkO7cZcp-0-ff5491be4d18bd8c846ed1442dc72768)
解析出前一个BroadcastReceiver存入结果中的key为first的消息。
在AndroidManifest.xml中配置MyReceiver2的优先级为0,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_03.jpg?sign=1738838222-6CnFVfLyTVESmhwsuG4hA5AZM8WXNLxF-0-3ebc0faeafbb963035301219725449d0)
先注释掉abortBroadcast(),点击发送有序广播按钮,可以看到先显示第一个广播接收器中的内容,再显示第二个广播接收器中的内容。
运行程序,其结果如图2-13~图2-15所示。
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_04.jpg?sign=1738838222-IjYPrmyVWjKjeQAPU611HYQ0Q3rCnPTG-0-b1d2b216aa02a73352b324eca99bdd36)
图2-13 运行首页
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_05.jpg?sign=1738838222-L0NFeBltfYrvc8UPDiHqePUYAYD0u4Tv-0-2e211c29a9d46e6153286406d2770e23)
图2-14 消息存入
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_06.jpg?sign=1738838222-Qxrbk03rrQ8KIrrO1ySBE8P3DZXvEnIP-0-45067ba2238f52290a506c5bde1aa94f)
图2-15 消息接收
根据上面的配置可以看出,该程序中包含两个Broadcast Receiver,其中MyReceiver的优先级更高,MyReceiver2的优先级略低。