一般情况下,onReceive 在 UI 线程执行,但也可以在 registerReceiver 时用 Handler 指定运行的线程。
Manifest-declared receivers 与 Context-registered receivers 的区别
| Manifest-declared | Context-registered | |
|---|---|---|
| 注册时机 | 应用安装时 | 调用 registerReceiver 时 | 
| 生命周期 | onReceive 被调用期间有效 | 受注册时所用的 Context 生命周期影响 | 
对进程状态的影响
在调用 onReceive 时,所在进程被当作 foreground process;onReceive 返回后,所在进程会被当作普通进程,如果进程里只有一个 manifest-declared receiver,它会被当作低优先级进程。
执行时间长一点的任务
- 使用 goAsync 结合 AsyncTask 等; - 执行时间仍然不要超过 10 秒。 
- 使用 JobScheduler 调度 JobService。 
发送广播
- sendOrderedBroadcast - 发送有序广播,一次只有一个 receiver 接收到,接收的顺序由 intent-filter 的 - android:priority属性决定,相同优先级的则随机顺序接收;一个 receiver 接收到有序广播后可以选择中止传递,也可以继续传递,可以携带它处理的结果。
- sendBroadcast - 发送无序广播,可以同时发送给所有 receiver,这更高效,但无法得到其它 receiver 处理的结果。 
- LocalBroadcastManager.sendBroadcast - 发送应用内广播。因为没有跨进程通信,这更高效更安全。 
可以通过 Intent.setPackage 来限制发送广播给哪些应用。
权限
发送广播时添加权限,则只有已经被授予了对应权限的 receiver 可以接收到;
注册 receiver 时添加权限,则只有已经被授予了对应权限的应用可以发送广播给该 receiver。
安全考虑与最佳实践
- 如果只是应用内收发广播,使用 LocalBroadcastManager。 
- 如果许多应用关注一个广播,那可能发出广播时造成许多应用启动,引起性能用用户体验的问题。这种情况下使用 context-registered receivers 会更好。比如 CONNECTIVEY_ACTION 广播只被分发给 context-registered receivers。 
- 不要用隐式意图广播敏感信息。可以使用如下方法来限制接收者: - 发送广播时指定权限; 
- 在 Android 4.0 以上,可以使用 Intent.setPackage(String) 来限制接收的应用; 
- 使用 local broadcasts。 
 
- 可能有恶意广播来触发你的 receivers。可以使用如下方法来限制收到到的广播: - 注册 receiver 时指定权限; 
- manifest-declared receivers 可以设置 android:exported 为 false 来屏蔽应用外的广播。 
- 使用 local broadcasts。 
 
- broadcast 的 actions 命名空间是全局的,使用一个你独有的命名空间,像包名一样。 
- onReceive 一般运行在主线程,所以要迅速返回。如果想执行稍长时间的任务,慎重启动后台服务,因为在 onReceive 返回后整个进程的存活时间无法保证。建议的方法是: - 在 onReceive 里调用 goAsync 然后将 BroadcastReceiver.PendingResult 传给后台线程,这将让该广播在 onReceive 返回后仍处于激活状态。这只是一个防止 UI 线程卡顿的方法,所有工作还是要在 10 秒内完成(普通 receiver 如果超过 10 秒还没返回会被系统认为阻塞了,会杀掉它)。 
- 使用 JobScheduler。 
 
- 不要在 onReceive 里启动 Activity,用户体验很差,特别是有多个 receiver 都这么干的时候。可以考虑使用 notification。 
文档信息
- 本文作者:Yingqi Chen
- 本文链接:https://cyq1005658400.github.io/blog//wiki/android-components-broadcastreceiver/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)