注:以下内容基于Android API Version 27(Android 8.1)Linux Kernel 3.18.0
Android的Broadcast本质上是一种事件的订阅和发布机制。订阅者通过registerReceiver
或者Manifest声明订阅消息,发布者通过sendBroadcast
发送消息,消息由AMS(ActivityManagerService)
进行和管理和派发,AMS充当消息中心的角色。
Broadcast注册和发送
动态注册
代码调用registerReceiver
在注册方进程这一侧会创建一个android.app.LoadedApk.ReceiverDispatcher.InnerReceiver#InnerReceiver
的实例,InnerReceiver
实际是一个binder本地对象(BBinder),实现了IIntentReceiver
接口,注册过程最终将IIntentReceiver
对象和注册所传的IntentFilter
对象发送给AMS。
AMS记录IIntentReceiver
、IntentFilter
和注册的进程ProcessRecord
,并建立起它们的对应关系。
当有广播发出时,AMS根据广播intent
所携带的IntentFilter
找到IIntentReceiver
和ProcessRecord
,然后回调App的ApplicationThread
对象的scheduleRegisteredReceiver
,将IIntentReceiver
和广播的intent
一并传给App,App直接调用IIntentReceiver
的performReceive
。
因为广播是通过binder线程回调到接收进程的,接收进程通过ActivityThread
里的H
这个Handler将调用转到主线程,然后回调BroadcastReceiver
的onReceive
。
静态注册
静态注册是通过在Manifest文件中声明实现了BroadcastReceiver
的自定义类和对应的IntentFilter
,来告诉PMS(PackageManagerService)这个App所注册的广播。
当AMS接收到广播后,会查找所有动态注册的和静态注册的广播接收器,静态注册的广播接收器是通过PMS(PackageManagerService)发现的,PMS找到对应的App,然后判断App所在的进程是否活着,如果活着直接调用App的ApplicationThread对象的scheduleReceiver
,如果进程未存活则先启动App进程,App进程启动后回调AMS的attachApplication
,attachApplication
则继续派发刚才的广播,派发方式同样是调用App的ApplicationThread对象的scheduleReceiver
,App这边收到调用后会先通过Handler转到主线程,然后根据AMS传过来的参数实例化广播接收器的类,接着调用广播接收器的onReceive
。
粘滞广播(Deprecated)
广播支持粘滞效果,也就是说在没有找到任何接收方的情况下,AMS会将广播保存起来,待有进程注册了这个广播的接收器,就立即将广播派发给它。
粘滞广播通过sendStickyBroadcast
发送。
有序广播
广播接收器可以声明prority属性,指定一个整数值代表接收器的优先级,AMS会根据接收器的优先级按顺序发送广播给接收器,接收器里可以通过调用abortBroadcast
终止广播派发流程,这样后面的接收器就收不到广播了。
有序广播通过sendOrderedBroadcast
发送。
LocalBroadcast
普通广播是一定要走AMS的,由AMS统一进行派发,这样的坏处是由于涉及到进程间通信,效率会有所损失。如果我们只想在进程内进行广播发送,可以使用LocalBroadcast
,LocalBroadcast
完全不涉及进程间通信。
LocalBroadcast
通过LocalBroadcastManager
来使用和管理,LocalBroadcastManager
的实现原理非常简单,它基于观察者模式,广播接收者注册一个BroadcastReceiver
对象实际上就是注册了一个callback,LocalBroadcastManager
内部会维护所有接收器引用,发送方发送时,LocalBroadcastManager
通过action
找到所有的接收器对象,然后通过Handler异步回调接收器的onReceive
。这样的实现是典型的消息总线模型。
Android 8.0对隐式广播的限制
隐式广播的定义:不针对特定应用的系统广播(比如ACTION_BOOT_COMPLETED
和CONNECIVITY_ACTION
)和由App发送的不指定包名的全局广播。
由于8.0之前隐式广播可以通过静态的方式注册,导致一些系统广播发出后引起大量App进程被拉起,严重影响了系统性能。8.0开始Android禁用了隐式广播。
注意事项
- 广播的发送是异步的,也就是说调用
sendBroadcast
后函数立即返回,不关心接收方是否接收到。 - 广播的接收回调函数
onReceive
是在主线程执行。 - 广播基于binder机制,所以传递数据大小受binder的数据缓冲区大小限制,也就是1M-8K。