![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=1738776627-83lXjGjDXX9SSXKYS0r1vCIkOeN3gR2e-0-f96b84917d934ff7f8df39bda99c017d)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_01.jpg?sign=1738776627-FuD14laD1dd0jw0arSQBbxqQ1UPSmMBW-0-20c600844af73f9d794c44af11092eb7)
4.系统广播(Local Broadcast)
Android内置了很多系统广播,当使用广播时,只需要在注册广播接收者时定义相关的Action即可,并不需要收到发送广播,当系统有相关操作(如开机、电池电量不足、拍照、网络变化等)时就会自动开启广播。
例如消息推送服务,需要实现开机启动的功能。要实现这个功能,就可以订阅系统“启动完成”这条广播,接收到这条广播后就可以启动自己的服务了。主要配置如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_02.jpg?sign=1738776627-5hlrxFEsON5M2IL8NLxhxN3TM2CmOv38-0-ee0ebbb3b86486b17fc6c6ab47967372)
同时从安全角度考虑,系统要求必须声明接收开机启动广播的权限,于是再声明使用下面的权限,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_03.jpg?sign=1738776627-4zYmAUVokRwixsH7KynSJlqdArrxq29K-0-416ae8b3e5c9076e4e84e449bb02b4f9)
2.4.4 BroadCastReceiver实现机制
广播接收器注册的方式分为两种:静态注册、动态注册。
1.静态注册
在AndroidManifest.xml里通过<receive>标签声明。
它的优点:不受其他组件生命周期影响,即使应用程序被关闭,也能接收广播;缺点:耗电,占内存;适用场景:需要时刻监听的广播。静态注册代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/55_04.jpg?sign=1738776627-9uXmjwza1NvD8F2xlUzkU65l21nXVTcA-0-8261e641e7466c348fcd6964fe6eb9f6)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_01.jpg?sign=1738776627-MUfaF6mkQ7m0Gaq2Evt69hkvWCrYusRB-0-462b6807d2245f1c89ee31423146632c)
注册示例:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_02.jpg?sign=1738776627-fmLS2gsU0eOxh5k2aMPB4pneU4qZLxJD-0-3c4bc21a9e1163e6ebe533ea144ce6bf)
当此App首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。
2.动态注册
在代码中通过调用Context的registerReceiver()方法进行动态注册BroadcastReceiver。它的优点是:灵活,不耗电,易控,省内存;缺点:需要手动注销;适用场景:需要特定时候监听的广播。具体代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/56_03.jpg?sign=1738776627-pETiAQwdgZrVFqm7xjqwqTkrBMiwUljk-0-af1a21ba6e09fa277f095115ad66774a)
对于动态广播来说,有注册必然就有注销,需要成对出现。重复注册注销或者注册忘了注销都不行,后者会报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=1738776627-kWaINSQA1Jyj3JSQ3RHFmxGymPJHjERH-0-9d90b522e803c211f4002d13cc4b21db)
【例2-9】 布局文件
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/57_02.jpg?sign=1738776627-5uLpWc6I3KTn9pghUgq8nGNIHVh6xC3K-0-5bb83968d843435e737bc4b77aa0a2ba)
对于设置条件,我们需要在AndroidManifest.xml中相应的目标下配置相同的条件,具体会在代码中说明。接下来附上主要代码。
新建项目,在MainActivity中添加下面代码,下面代码的主要功能是在主Activity中实现发送广播部分,主要代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/57_03.jpg?sign=1738776627-1qL9CwQf3xvLWxd5a3OVbc4j6vJXNV0Z-0-4d1de2f6db47e8298cf2f5b8d2046993)
新建一个类Receiver,用于接收发送出来的消息:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_01.jpg?sign=1738776627-NRZU7w71tAHMeVlNcqWltHxOKbFtY6qf-0-bb713e409bb477fc0bafadd1e92df600)
最后还有最重要的一步,在AndroidManifest.xml配置Receiver类的广播接收意图:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_02.jpg?sign=1738776627-4x8qRxrH8l33Dsd21B44sFYhSaKS7PHv-0-adfea88564d277ae772540d39bec1b2f)
至此,对于普通广播的实现就完成了,运行后,在输出入日志中可以看到如下信息:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_03.jpg?sign=1738776627-RwjRdOxyArAiA24yygZJFMShgcGc2sZS-0-15c59aead2bdb9650ae15303f8079aa8)
有序广播(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=1738776627-tcBOmK6wnl9viGnNqdlm9R6D5hE2HULM-0-7551f63bd925a534f8a5fa5003087eb4)
发送有序广播:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_05.jpg?sign=1738776627-M3OqeKvtDJfc49WloR1bCzAHzoshLUSq-0-e7b4d81be3090799a02ea043567ab01d)
发送有序广播的第二个参数是一个权限参数,如果为null则表示不要求BroadcastReceiver声明指定的权限。如果不为null,则表示接收者若想要接收此广播,需要声明指定的权限(为了安全)。
以上发送有序广播的代码需要在AndroidManifest.xml中自定义一个权限:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/58_06.jpg?sign=1738776627-TVRS0bGMs2H1MHJG9SeqBvTvhMNlj2It-0-e32e6c1eb20234a57fe682a5f0c2337d)
然后声明使用了此权限:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_01.jpg?sign=1738776627-9sf0ZtxVLkPIlLdJLBFgZJUPrixtKxdq-0-661d1d88428f468c9635ff300873b528)
终止广播需要在优先级高的BroadcastReceiver的onReceiver()方法中添加代码:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_02.jpg?sign=1738776627-inZLR9iDto04arXFw4uPDbJcqI4iHQHh-0-c2b3c6951789866989adc3217db8efa5)
则广播将不会再继续往下传播,即在低优先级的BroadcastReceiver中将不会再接收到广播消息。
【例2-10】 建立MainActivity
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_03.jpg?sign=1738776627-puQElkuisIbHV3yNFMqmdWzXXWmQSYdq-0-d35f6ca47c8fe12f191ff25190508e2e)
代码中指定了Intent的Action属性,再调用sendOrderedBroadcast()方法来发送有序广播。对于有序广播,它会按优先级依次触发每个BroadcastReceiver的onReceiver()方法。
第一个BroadcastReceiver代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_04.jpg?sign=1738776627-dXXDyPZkW7dd8ysuADWkeYx8fenGfjCz-0-4249ec11dd68c6091ac553c8f7dd4b02)
MyReceiver不仅处理了它所接收的消息,而且向处理结果中存入了key为first的消息,这个消息将可以被第二个BroadcastReceiver解析出来。
abortBroadcast()用于取消广播,如果这条代码生效,那么优先级比MyReceiver低的BroadcastReceiver都将不会被触发。
在AndroidManifest.xml中部署该BroadcastReceiver,并指定其优先级为20,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/59_05.jpg?sign=1738776627-TM1hDYPt7v2d6WjK19FjgrkqqCurzaT2-0-0540fae98484a2e57863031a8d386fe0)
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_01.jpg?sign=1738776627-ON7qU89UvWshmIb7J49WYmpAdaize31x-0-5bf3ec954997f0ed894e28050a881110)
接下来提供第二个BroadcastReceiver,将会解析前一个BroadcastReceiver存入的key为first的消息,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_02.jpg?sign=1738776627-0RJ1jQ1FIc3gjd9L97rGiKQttjjbjYDA-0-d80cbe4b7c7eb6bff29dab5bf587fc1a)
解析出前一个BroadcastReceiver存入结果中的key为first的消息。
在AndroidManifest.xml中配置MyReceiver2的优先级为0,代码如下:
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_03.jpg?sign=1738776627-0o9QqcBx3loWp87pWDfdtXgA5PmTCm7I-0-b43e1da7e573571a744bdab9b20f5c15)
先注释掉abortBroadcast(),点击发送有序广播按钮,可以看到先显示第一个广播接收器中的内容,再显示第二个广播接收器中的内容。
运行程序,其结果如图2-13~图2-15所示。
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_04.jpg?sign=1738776627-AY2y0mzY31OCU0c45wLKd2RX07WUD4Qp-0-807c689921dc64ba9aea9325d680025e)
图2-13 运行首页
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_05.jpg?sign=1738776627-PMS9ccoSs9Ztt2aQbVFbJQ9U0jiktJFM-0-d6db07250ec05e0a75cbf8073c024a88)
图2-14 消息存入
![](https://epubservercos.yuewen.com/B95AF2/21122066801630906/epubprivate/OEBPS/Images/60_06.jpg?sign=1738776627-bP31yxZEyfeWysSlGWovIiPDRHzeicNu-0-e87f485b32923e06b5c91b3502c1b4a0)
图2-15 消息接收
根据上面的配置可以看出,该程序中包含两个Broadcast Receiver,其中MyReceiver的优先级更高,MyReceiver2的优先级略低。