【Android基础组件】BroadCast——广播三两事
本文介绍了Android四大组件之一的广播的相关基础理论知识,想了解更多可参阅安卓官方文档或技术书籍。
什么是广播?
一般来说,广播可作为跨应用和普通用户流之外的消息传递系统。Android 应用与 Android 系统和其他 Android 应用之间可以相互收发广播消息,这与发布-订阅设计模式相似。这些广播会在所关注的事件发生时发送。应用也可以注册接收特定的广播。广播发出后,系统会自动将广播传送给同意接收这种广播的应用。
举栗时间
- Android 系统会在发生各种系统事件时发送广播——如系统启动或设备开始充电时。
- 应用可以发送自定义广播来通知其他应用它们可能感兴趣的事件——如一些新数据已下载。
有哪些广播?
本地广播与全局广播
- 本地广播,只能够在应用程序的内部进行传递 ,并且相应广播接收器也只能接收来自应用程序发出的广播。
- 全局广播便是能在应用之间进行传递的广播。相比之下,本地广播的的效率更高(无需进行进程间通信),而且您无需担心其他应用在收发您的广播时带来的任何安全问题。
标准广播与有序广播
- 标准广播是一种完全异步执行的广播,广播发出后所有的BroadcastReceiver几乎会在同一时刻收到这条广播消息。这意味着该广播的效率比较高,但无法被截断。
- 有序广播则是一种同步执行的广播,广播发出后,同一时间只有一个接收器会收到广播消息,在执行完onReceive方法后,广播才会继续传递。这意味着此时的接收器是有先后顺序的,并且在前面收到广播的接收器还可以对正在传递的广播进行截断(调用abortBroadcast方法)。接收器的运行顺序可以通过匹配的 intent-filter 的 android:priority 属性来控制;具有相同优先级的接收器将按随机顺序运行。
隐式广播与显式广播
- 隐式广播:未指定发送给哪个应用程序的广播。注意,在Android 8.0之后的系统中,静态注册的BroadcastReceiver是无法接受除少数特殊的系统广播之外的隐式广播的。
- 显式广播:经由setPackage()方法指定了目标程序的广播。
系统广播
系统广播指的是系统在发生各种系统事件时(如系统进入和退出飞行模式时)自动发送的广播。
广播怎么用?
广播的使用主要包括以下几个步骤:
- 编写广播,实现onReceive方法中的逻辑
- 在APP中对所需接收的广播进行注册
- 在某处发送广播
sendOrderedBroadcast(Intent, String);
sendBroadcast(Intent);
LocalBroadcastManager.sendBroadcast();
- 在不再需要该广播时取消注册
怎么注册广播?
- 静态注册:清单声明的接收器(系统将在广播发出后启动应用)
系统软件包管理器会在应用安装时注册接收器。然后,该接收器会成为应用的一个独立入口点,这意味着如果应用当前未运行,系统可以启动应用并发送广播。 - 动态注册:代码中调用registerReceiver(BroadcastReceiver, IntentFilter)注册的接收器
如注册的是本地广播,请调用 LocalBroadcastManager.registerReceiver(BroadcastReceiver, IntentFilter)。 注意:需要停止接收广播时,需调用unregisterReceiver(BroadcastRecveiver),特别是在不再需要接收器或上下文不再有效时,务必取消注册。请注意注册和注销接收器的位置,比方说,如果您使用 Activity 上下文在 onCreate(Bundle) 中注册接收器,则应在 onDestroy() 中注销,以防接收器从 Activity 上下文中泄露出去。如果您在 onResume() 中注册接收器,则应在 onPause() 中注销,以防多次注册接收器(如果您不想在暂停时接收广播,这样可以减少不必要的系统开销)。请勿在 onSaveInstanceState(Bundle) 中注销,因为如果用户在历史记录堆栈中后退,则不会调用此方法。
接收器的工作时间
接收器的工作时间与注册的上下文有关,只要上下文有效,接收器便能持续工作。
举栗时间
- 如Activity中创建的接收器,只要Activity未被销毁,接收器便能收到广播。
- 在应用上下文中注册的接收器,只要应用在运行,便可以收到广播。
使用时需要注意什么?
不可在onReceive方法中启动长时间运行的后台线程
当 BroadcastReceiver 在其 BroadcastReceiver.onReceive() 方法中接收到一个 Intent 时,它会启动一个线程,然后从该函数返回。一旦返回,则系统会认为 BroadcastReceiver 不再处于活动状态,因此不再需要其托管进程(除非其中有其他应用组件处于活动状态)。因此,系统可能会随时终止进程以回收内存,这样会终止在进程中运行的衍生线程。 如要执行长时间运行的工作:
- 在接收器的 onReceive() 方法中调用 goAsync(),并将 BroadcastReceiver.PendingResult传递给后台线程。这样,在从 onReceive()返回后,广播仍可保持活跃状态。不过,即使采用这种方法,系统仍希望您非常快速地完成广播(在 10秒以内)。为避免影响主线程,它允许您将工作移到另一个线程。
- 使用 JobScheduler 调度作业,这样系统就知道进程中还有处于活动状态的任务正在进行中。
如果您不需要向应用以外的组件发送广播,就使用LocalBroadcastManager 来收发本地广播。
LocalBroadcastManager 效率更高(无需进行进程间通信),并且您无需考虑其他应用在收发您的广播时带来的任何安全问题。本地广播可在您的应用中作为通用的发布/订阅事件总线,而不会产生任何系统级广播开销。
优先使用动态注册而不是静态注册
如果有许多应用在其清单中注册接收相同的广播,可能会导致系统启动大量应用,从而对设备性能和用户体验造成严重影响。有时,Android 系统本身会强制使用动态注册的接收器。例如,CONNECTIVITY_ACTION 广播只会传送给动态注册的接收器。
请勿使用隐式 intent 广播敏感信息
任何注册接收广播的应用都可以读取这些信息。您可以通过以下三种方式控制哪些应用可以接收您的广播:
- 可以在发送广播时指定权限。
- 在 Android 4.0 及更高版本中,可以在发送广播时使用 setPackage(String)指定软件包。系统会将广播限定到与该软件包匹配的一组应用。
- 可以使用 LocalBroadcastManager 发送本地广播。
当您注册接收器时,任何应用都可以向您应用的接收器发送潜在的恶意广播
您可以通过以下三种方式限制您的应用可以接收的广播:
- 可以在注册广播接收器时指定权限。
- 对于静态注册的接收器,您可以在清单中将 android:exported 属性设置为“false”。这样一来,接收器就不会接收来自应用外部的广播。
- 可以使用 LocalBroadcastManager 限制您的应用只接收本地广播。
广播操作的命名空间是全局性的
请确保在您自己的命名空间中编写操作名称和其他字符串,否则可能会无意中与其他应用发生冲突。
请勿从广播接收器启动 Activity,否则会影响用户体验,尤其是有多个接收器时
相反,可以考虑显示通知。
拓展——发布/订阅模式与观察者模式区别
笔者在初次接触广播时误以为广播使用的是观察者模式,实则不然,而二者最大的区别在于调度的地方不同,观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调度的。 文末放上发布/订阅模式与观察者模式的对比博客,有疑惑的可了解:【设计模式(三):观察者模式与发布/订阅模式区别】https://www.cnblogs.com/lovesong/p/5272752.html
|