Android 广播Broadcast原理总结

5,153 阅读4分钟

注:以下内容基于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记录IIntentReceiverIntentFilter和注册的进程ProcessRecord,并建立起它们的对应关系。

当有广播发出时,AMS根据广播intent所携带的IntentFilter找到IIntentReceiverProcessRecord,然后回调App的ApplicationThread对象的scheduleRegisteredReceiver,将IIntentReceiver和广播的intent一并传给App,App直接调用IIntentReceiverperformReceive

因为广播是通过binder线程回调到接收进程的,接收进程通过ActivityThread里的H这个Handler将调用转到主线程,然后回调BroadcastReceiveronReceive

静态注册

静态注册是通过在Manifest文件中声明实现了BroadcastReceiver的自定义类和对应的IntentFilter,来告诉PMS(PackageManagerService)这个App所注册的广播。

当AMS接收到广播后,会查找所有动态注册的和静态注册的广播接收器,静态注册的广播接收器是通过PMS(PackageManagerService)发现的,PMS找到对应的App,然后判断App所在的进程是否活着,如果活着直接调用App的ApplicationThread对象的scheduleReceiver,如果进程未存活则先启动App进程,App进程启动后回调AMS的attachApplicationattachApplication则继续派发刚才的广播,派发方式同样是调用App的ApplicationThread对象的scheduleReceiver,App这边收到调用后会先通过Handler转到主线程,然后根据AMS传过来的参数实例化广播接收器的类,接着调用广播接收器的onReceive

粘滞广播(Deprecated)

广播支持粘滞效果,也就是说在没有找到任何接收方的情况下,AMS会将广播保存起来,待有进程注册了这个广播的接收器,就立即将广播派发给它。

粘滞广播通过sendStickyBroadcast发送。

有序广播

广播接收器可以声明prority属性,指定一个整数值代表接收器的优先级,AMS会根据接收器的优先级按顺序发送广播给接收器,接收器里可以通过调用abortBroadcast终止广播派发流程,这样后面的接收器就收不到广播了。

有序广播通过sendOrderedBroadcast发送。

LocalBroadcast

普通广播是一定要走AMS的,由AMS统一进行派发,这样的坏处是由于涉及到进程间通信,效率会有所损失。如果我们只想在进程内进行广播发送,可以使用LocalBroadcastLocalBroadcast完全不涉及进程间通信。

LocalBroadcast通过LocalBroadcastManager来使用和管理,LocalBroadcastManager的实现原理非常简单,它基于观察者模式,广播接收者注册一个BroadcastReceiver对象实际上就是注册了一个callback,LocalBroadcastManager内部会维护所有接收器引用,发送方发送时,LocalBroadcastManager通过action找到所有的接收器对象,然后通过Handler异步回调接收器的onReceive。这样的实现是典型的消息总线模型。

Android 8.0对隐式广播的限制

隐式广播的定义:不针对特定应用的系统广播(比如ACTION_BOOT_COMPLETEDCONNECIVITY_ACTION)和由App发送的不指定包名的全局广播。

由于8.0之前隐式广播可以通过静态的方式注册,导致一些系统广播发出后引起大量App进程被拉起,严重影响了系统性能。8.0开始Android禁用了隐式广播。

注意事项

  • 广播的发送是异步的,也就是说调用sendBroadcast后函数立即返回,不关心接收方是否接收到。
  • 广播的接收回调函数onReceive是在主线程执行。
  • 广播基于binder机制,所以传递数据大小受binder的数据缓冲区大小限制,也就是1M-8K。

参考
blog.csdn.net/Luoshengyan…