Broadcast知识详解
今天来看下Android四大组件之一的Broadcast。
一、什么是Broadcast
Android apps can send or receive broadcast messages from the Android system and other Android apps, similar to the publish-subscribe design pattern. These broadcasts are sent when an event of interest occurs.
Android应用可以发送或接收来自 Android 系统和其他 Android 应用的广播信息,广播类似于发布订阅设计模式,它们是在有兴趣的事件发生时发送的。
广播采用观察者模式:基于消息的发布 / 订阅事件模型 将广播的发送和接收进行解耦。
基本原理:
1、广播接收者通过 Binder 机制在 AMS( Activity Manager Service ) 注册;
2、广播发送者通过 Binder 机制向 AMS 发送广播;
3、AMS 根据广播发送者要求,在已注册列表中,寻找合适的 BroadcastReceiver ( 寻找依据:IntentFilter / Permission );
4、AMS 将广播发送到 BroadcastReceiver 相应的消息循环队列中;
5、广播接收者通过消息循环拿到此广播,并回调 onReceive() 方法。
需要注意的是:广播的发送和接收是异步的,发送者不会关心有无接收者或者何时收到。
二、广播的类型
按是否有序分类:
-
普通广播:也叫标准广播,是一种完全异步执行的广播。
在广播发出之后,所有广播接收器几乎都会在同一时刻接收到这条广播消息,它们之间没有任何先后顺序,广播的效率较高。
优点: 完全异步, 逻辑上可被任何接受者收到广播,效率高
缺点: 接受者不能将处理结果交给下一个接受者, 且无法终止广播. -
有序广播:是一种同步执行的广播。
在广播发出之后,同一时刻只有一个广播接收器能够收到这条广播消息,当其逻辑执行完后该广播接收器才会继续传递。优先级高的可以较早接收到广播并可以添加信息或截断广播
按是否能远端通信
-
本地广播:将export属性设置为false即为本地广播,这样广播就只能在app内收到。
优点:1、其他的 APP 不会受到局部广播,不用担心数据泄露的问题。
2、其他 APP 不可能向当前的 APP 发送局部广播,不用担心有安全漏洞被其他 APP 利用。
3、局部广播比通过系统传递的全局广播的传递效率更高。 -
普通广播:将export属性设置为true,广播可以跨进程收到。
按是否是系统定义
-
系统广播:系统内置了很多广播,比如电话、短信来临时都会发出广播
-
自定义广播:应用根据需求自定义的广播
三、广播的接收
两种注册方式及区别
我们都知道四大组件都需要注册后才能使用。Broadcast提供了两种方式进行注册分别是静态注册和动态注册。
静态注册
在mainfest中通过
相关属性:
- android:exported 是否能接收其他app发送的广播
默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false - android:permission 广播发送权限
- android:process广播运行所在进程
动态注册
通过context.registerReceiver代码注册。
区别:
动态注册较为灵活,但是是非常驻的 并且需要进程启动后才会接收,
静态注册是常驻的,进程未启动的情况下也可以接收广播
BroadcastReceiver
广播接收者当收到对应的已注册广播时会回调其onReceive函数。
不过需要注意的是:
使用静态注册的方法注册当接收到对应广播时系统会创建一个新的 BroadcastReceiver 组件对象来处理它接收到的每个广播。 此对象仅在调用 onReceive 函数内部有效。 一旦代码从此方法return,系统将认为组件不再活动。也就是说此方法每次接收广播都会创建新的Receiver 处理完广播后该Receiver 会销毁,下次接收会再次重新创建Receiver 。
使用动态注册接收者只要其注册上下文有效就可以接收广播。 例如,如果在一个activity上下文中注册,那么只要activity没有被销毁,您就会收到广播。 如果您在应用程序上下文中注册,那么只要应用程序运行,您就会收到广播。
Broadcastreceiver对进程状态的影响
因为在触发其onReceive 方法时系统会认为当前进程属于前台进程从而保持较高的优先级,但是onReceive完成后其优先级可能会降低如果此时系统资源紧张有可能进程会被系统杀死以回收资源。
因此不应该从广播接收器启动长时间运行的后台线程。 在 onReceive ()之后,系统可以在任何时候终止进程以回收内存,并且在这样做时,它终止了在进程中运行的派生线程。 为了避免这种情况,您应该调用 goAsync ()(如果需要多一点时间在后台线程中处理广播) ,或者使用 JobScheduler 从接收方调度一个 JobService,这样系统就知道该进程继续执行活动工作。
四、广播的发送
广播的发送有3种方式它们分别适用于不同类型的Broadcast:
- sendOrderedBroadcast(Intent, String)
此方式用来发送有序广播,广播的传递顺序是按接收器的优先级又高到低进行的(优先级intent-filter 的 android: priority 属性来控制),优先级高的会先接收到广播并可以截断以让广播终止发送,也可以将结果传递给下一个广播。 - sendBroadcast(Intent)
正常广播以未定义的顺序向所有接收器发送广播,但是意味着接收者不能从其他接收者读取结果,传播从广播接收的数据,或者中止广播 - LocalBroadcastManager.sendBroadcast
发送本地广播方法,广播到发送者与接收器在同一个应用程序中。 如果你不需要跨进程发送广播,使用本地广播这样实现效率更高(不需要进程间通信) ,您不需要担心与其他应用程序能够接收或发送您的广播有关的任何安全问题
发送带权限的广播
通过sendBroadcast (Intent,String)发送广播可以指定一个权限参数。 只有在清单中使用标记请求该权限的接收者(如果该权限是危险的,则随后被授予该权限)才能接收广播,这样可以防止我们的广播被不该接收的应用接收。
五、发送和接收广播的一些安全考虑和最佳做法
- 首先如果不需要跨应用接收、发送广播那么尽量使用LocalBroadcastManager,这样不仅效率高并且安全性也高
- 其次优先使用动态注册广播,因为如果很多应用程序在它们的清单中注册了相同的广播,当接收广播时会导致系统启动许多应用程序,从而对设备性能和用户体验产生重大影响。
- 不要使用隐式intent广播敏感信息。 任何注册接收广播的应用程序都可以读取这些信息。可以通过增加权限、 通过setPackage(String)设置接收广播的包名、使用本地广播来限制接收广播的应用。
- 同样的当你注册一个接收器,任何应用程序都可以发送潜在的恶意广播到你的应用程序的接收器,所以有必要限制应用接收的广播,可以通过在注册广播接收器时指定权限、在manifest 文件中将android:exported设置为false、使用本地广播等方法来限制。
- 广播操作的名称空间是全局的,所以在自定义action的时候有必要添加包前缀以防止跟其他应用冲突。
- 因为onReceive (Context,Intent)方法在主线程上运行,所以它应该快速执行并返回。 如果需要执行长时间运行的工作,请谨慎使用子线程或启动后台服务,因为在 onReceive ()返回后,系统可能会杀死整个进程。 因此建议:
如果希望延长执行时间可以使用goAsync()或JobScheduler但是即使这样也不建议在onReceive执行长时间的任务。 - 不要从广播接收器启动activity,因为用户体验不好; 特别是如果有多个接收器。 但是可以考虑显示一个通知。
六、相关源码分析
广播机制,从本质来说,它是一种消息订阅/发布机制,因此,使用这种消息驱动模型的第一步便是订阅消息;而对Android应用程序来说,订阅消息其实就是注册广播接收器。
注册广播(registerReceiver)过程源码分析
发送广播(sendBroadcast)的过程源码分析
本地广播( LocalBroadcastManager)源码解析
参考:
https://developer.android.com/guide/components/broadcasts