一.简介
广播 ( Broadcast )是 Android 系统用于进程间或者线程间传播数据( 通过 Intent )的一种机制,与同样可在进程间通信的 Binder 不一样的是 Binder 的通信需要知道确切的信息接收方,而广播的消息不用知道信息的接收方,发送方只负责发送广播不管信息有无接收。通过设置 intenfilter 则可以指定广播相应的接收者。
广播的实现原理是订阅者模式,消息的发布者发布事件,使用者订阅自己想要接收的信息,发送者一般是 Context 的子类,比如 Activity ,Service 等,而接受者就是注册 BroadcastReceiver 的地方,广播的发送后需要寻找接收者 ,而接收者的的确定是在 ActivityManagerService 进行的,ActivityManagerService 扮演着广播中心的角色,负责系统中所有广播的注册和发布操作,具体流程如下:
二.几种分类
(一).BroadcastReceiver 的注册方式(是否常驻)
1.静态注册 (常驻)
在 AndroidManifest 中通过 < receiver > 注册的 BroadcastReceiver 属于静态的 BroadcastReceiver ,也是常驻型的,系统会在 APP 进程第一次启动的时候实例化 BroadcastReceiver,并在系统中注册该接收者,这种类型的接收者不受 APP 进程的影响,即如果进程未启动,也会收到广播并执行 onReceive 方法。 在 Android 3.1 后系统默认不把广播发送给未启动的进程。
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
2.动态注册 ( 非常驻 )
通过调用 registerReceiver 进行注册的属于非常驻型的,这种类型的接收者需要随着 Context (Activity 或 Service 等)的销毁使用 unregisterReceiver 进行注销。否则可能造成内存泄露。
(二).广播的类型
1.标准广播 ( 普通广播 )
通过 sendBroadcast()发送,这种广播的接收顺序是异步的,无序的,即有多个接收者它们的对广播的接收没有任何关联。sendBroadcastAsUser 这个方法可发送给指定的用户。对于动态注册的 Receiver ,可以实现异步接收,但是对于静态注册的 Receiver 只能是有序,串行接收,这样做是避免突然启动大量的进程。
2.有序广播
通过 sendOrderedBroadcast() 发送,这种广播对接收来说是有顺序的,通过 android:priority 属性可设置优先级 (-1000 ~ 1000 )优先级高的先接收,低的后接收,如果优先级一样则动态注册的优先,而且优先接收的还可以通过 abortBroadcast() 对广播进行拦截,则后续的接收者不会接收到挂广播。同样也可以通过 sendOrderedBroadcastAsUser 发送给指定的用户。
3.粘性广播
sendStickyBroadcast() 发送的是粘性广播,粘性广播在发送后就一直存在于系统的消息容器里面,等待对应的接收者去处理,如果暂时没有接收者处理这个消息则一直在消息容器里面处于等待状态,粘性广播的接收者如果被销毁,那么下次重建时会自动接收到消息数据。发送粘性广播需要添加权限
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
不过由于安全问题,在Android 5.0 ( API 21 )后已经失效,同样的还有序粘性广播。
4.系统广播
Android 系统内部也设置了广播,用于发送系统事件,比如 系统的启动,网络变化等。
5.本地广播
广播的发布和接收是可以跨进程的,这可能会造成一定的安全的问题,如果希望只接收本进程的广播可以以下有两种方式:
- (1).使用 LocalBroadcastManager 注册/注销 BroadcastReceiver,这种方式只动态注册,通过这种方式注册的接收者只会收到本进程发送的广播。
- (2). 将 android:exported 设为 false 也可达到同样的效果。
- (3).使用 < permission > 标签来声明自定义权限,自定义权限时必须同时指定 protectionLevel 属性值 (normal,dangerous,signature 和 signatureOrSystem )。
三.注意的几点
1.BroadcastReceiver 的 onReceive 的方法是在工作在主线程的,所以不要做一些耗时的工作,否则会造成 ANR ,也不要在里面创建子线程,这个时候 BroadcastReceiver 所在的进程没有任何的组件的话(常驻广播容易产生的问题,即进程启动但是没有任何组件在运行)这个进程容易被系统杀掉,相应的线程也会销毁。
2.在 Android 8.0 的平台上,应用不能对大部分的 BroadcastReceiver 进行静态注册否则不能收到广播,但是像系统级的广播还能收到广播。
3.有时需要注册一些广播的时候需要添加一定的权限,如监听网络变化的需要添加网络权限,监听短信的需要添加访问手机短信的权限。
四.工作过程
广播的工作过程同样涉及到 进程,c/s 架构等概念,这在Activity 的启动过程已经有阐述过,这里就只对一些涉及的新对象进行说明。
(一).客户端 (app 进程)
1.ReceiverDispatcher
广播分发器,用于接收广播并通过内部的一个 Handler 类型对象回到主线程调用 onReceive,一个 BroadcastReceiver 就有与之对应的一个 ReceiverDispatcher 对象。
2.InnerReceiver
ReceiverDispatcher 的内部类,这是实现 Binder 接口的对象,用于与系统进程间的通信。
3.LocalBroadcastManager
Android Support包提供的一个单例类,通过它注册的 BroadcastReceiver 和发送的广播只能在本应用内传播,无法收到其他应用发送的广播同时发送的广播也只能被本应用接收。内部封装了BroadcastReceiver 类(用于保存应用注册本地广播接收者)和 IntentFilter,通过 Hanlder 的方式发送广播。因为是单例类持有本应用进程的所有本地广播接收者所以发送的时候可针对性的发送 ,且使用 Hanlder 直接在本应用进程发送广播,不通过 AMS ,因此不会发送到其他的应用程序。大致情况如下:
(二). 服务端 (SystemService 进程)
1.BroadcastFilter
IntentFilter 在系统中对应的对象,IntentFilter 在系统中会转换为 BroadcastFilter 并保存起来。
2.ReceiverList
BroadcastReceiver 在系统中对应的对象,这是一个 List 集合,用于存放 BroadcastFilter ,因为一个 Receiver 可以有多个 IntentFilter。
3.RegisteredReceivers
这也是一个集合,保存着系统中所有 ReceiverList ,这是一个 HashMap ,以 InnerReceiver 在系统中的 Binder 对象 (receiver.asBinder()) 为 key , ReceiverList 作为 value 。同时这也保存着所有的 BroadcastFilter ,便于收到广播的时候进行查询。
4.BroadcastRecord
系统中的广播对象,包含着一个 Intent 和一个 BroadcastFilter 。
5.mParallelBroadcasts
并行广播队列,存放着需要处理的普通广播。
6.mOrderedBroadcasts
串行广播队列,存放着需要处理的有序广播。
7.PackageManagerService (PMS)
在广播中负责静态接收者的注册。
(一)动态注册
(二)静态注册
(三).发送和接收
参考资料:
《Android 开发艺术探索》
广播的注册、发送和接收过程
Android Broadcast广播机制分析
Android应用程序注册广播接收器(registerReceiver)的过程分析