Android Broadcast广播机制

概要

本文主要讲解广播的机制和使用,通过广播注册的两种方式比较了一下不同方式的优缺点,
简单阐述了一下广播的生命周期,对于广播的类型和收发也做了部分介绍,解释了一下Android不同版本中广播API的改变,最后用一个观察者模式对广播的实现原理进行了简明的阐释。

Android Broadcast是一种广泛用于应用程序之间传递消息的机制,也是Android系统的四大组件之一。广播机制包含三个基本要素:广播(Broadcast) – 用于发送广播;广播接收器(BroadcastReceiver) – 用于接收广播;意图内容(Intent)-用于保存广播相关信息的媒介。

广播分为两个方面:广播发送者和广播接收者,在Android系统中很多操作完成以后都会发送广播,比如说发送短息、打出一个电话、开机或者网络状态改变和电量改变等等,如果某些应用程序想要在这些操作完成以后做一些相应的处理,就可以对这些广播做接收。这个广播跟传统意义中的电台广播有些相似之处,只是传统电台广播发送的语音,而Android系统发送的是目的意图Intent。之所以叫广播,就是因为它只负责“说”而不管你“听不听”,也就不管接收方如何处理,当然了在接收方面也跟电台广播类似,如果错过特定时间点的广播,后续也就接收不到已经发送的广播了,过时不候,没有敬请期待了。。。

广播的使用

  • 同一App内部的同一组件内的消息通信(单个或多个线程之间);
  • 同一App内部的不同组件之间的消息通信(单个进程);
  • 同一App具有多个进程的不同组件之间的消息通信;
  • 不同App之间的组件之间消息通信;
  • Android系统在特定情况下与App之间的消息通信。

在这里我们看到Broadcast也可以在不同App应用之间进行消息的通信,如果我们开发一个应用需要在允许的情况下自动填充短信中的验证码,那么这时候要监听用户短信,短信和自己App就处在不同的进程之间。Activity和Service在某些情况下的通信也可以借助Broadcast,这时候就是在同一进程不同组件之间的消息通信。在Android的四大组件Activity、Service、Broadcast Receiver和Content Provider中,目前大概只有Activity不可以跨进程通信,其余的都可以在不同应用之间进行消息的通信,Service通过aidl,Broadcast在不同应用之间跟在同一个应用之间通信的处理方式大体相同,有时候大概只需要差异化配置一下intent-filter中的exported属性即可,Content Provider设计本身的出发点就是为了在不同应用之间来通信。

BroadcastReceiver

自定义BroadcastReceiver

一般情况下在使用广播通信时我们都会自定义一个广播接收器来处理通信,自定义广播接收器需要继承基类BroadcastReceivre,并实现抽象方法onReceive(context, intent)方法。广播接收器接收到相应广播后,会自动回调onReceive方法。默认情况下,广播接收器也是运行在UI线程,因此,onReceive方法中不能执行太耗时的操作,否则将因此ANR。

下面代码就是一个自定义的广播接收器:

BroadcastReceiver的注册类型

BroadcastReceiver有两种注册类型:静态注册和动态注册,在Android四大组件中,也是唯一一个可以在代码中注册的组件,其余的三个组件都必须在android清单文件中注册。

静态注册

静态注册就是在Android清单文件AndroidManifest.xml中进行注册,规则如下:

  • android:exported ——此broadcastReceiver能否接收其他App的发出的广播,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);
  • android:name —— 此broadcastReceiver类名;
  • android:permission ——如果设置,具有相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收;
  • android:process ——broadcastReceiver运行所处的进程。默认为app的进程。可以指定独立的进程(Android四大基本组件都可以通过此属性指定自己的独立进程)

下面是一个简单的注册示例:

其中,intent-filter由于指定此广播接收器将用于接收特定的广播类型,静态注册有这样一个特点,即是App没有启动或者已经关闭之后,BroadReceiver仍然会继续运行,这种实现机制会给用户造成困扰,而且如果一个App中使用这种方式注册了大量的广播,会降低整个手机的运行速度。当然了这种方式也是有它的用处的,有某些程序是想使用强制开机时启动的,在这种情况下这种机制就满足了需求,注册了开机启动的App,在开机时程序根本就未曾启动过,这时候可以却接收到开机启动的广播,一旦接受到广播我们就可以在onReceiver方法中来执行程序的启动,这就是开机启动的机制。但是现在在Android3.1及其更高版本的系统中该种实现方式大概就可能不再成立了,下面将会细讲。

动态注册

动态注册就是在应用程序代码中注册,该种方式采用了一种更灵活的方式完成了BroadcastReceiver的绑定和解除绑定的操作,主要是借助于Context类中的两个函数:

典型的写法如下:

在Activity实例化时会动态将RegisterRecever注册到系统中,当Activity销毁时,解除注册,此时RegisterRecever将不会再收到广播,除非重新注册。

广播的类型及收发

根据广播的发送方式,尅将其分为以下几种方式:

普通广播

此处将广播界定为开发者定义的广播,普通广播会被已经注册相应intent-filter接收,且顺序是无序的。如果发送广播时有相应的权限要求,BroadCastReceiver如果想要接收此广播,也需要有相应的权限。

系统广播

Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

有序广播

有序广播的有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast。

有序广播主要特点如下:

  • 多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,priority值取值范围从-1000到1000,值越大收到广播的顺序越靠前。对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。
  • 先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的 BroadcastReceiver不再接收到此广播,可以被截断原始值是更改不了的,但是却可以添加附加值,这样后续的广播接收者也可以获取上一个接收者添加的附加值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。

粘性广播

在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated,此处不再多做总结。

App应用内广播

由前文阐述可知,Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:

  • 其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
  • 其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。

无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:

  1. 对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
  2. 在广播发送和接收时,都增加上相应的permission,用于权限验证;
  3. 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
  4. 发送广播时直接显示启动广播,就如同显示启动activity一样;

该种方式跟第三种方式类似,直达方式直接广播给RegisterReceiver,而且在这种情况下如果设置了IntentFilter,发送广播时会忽略IntentFilter,所以才是直达。

App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。

相比于全局广播,App应用内广播优势体现在:

  • 安全性更高;
  • 更加高效。

为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。

对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。

广播的生命周期

广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态。在Android系统中只要5秒钟无响应就会弹出ANR,广播也是在主线程中运行的,当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应会弹出ANR。

拥有一个活跃状态的广播接收器的进程被保护起来而不会被杀死,但仅拥有失活状态组件的进程则会在其它进程需要它所占有的内存的时候随时被杀掉。所以,如果响应一个广播信息需要很长的一段时间,我们一般会将其纳入一个衍生的线程中去完成,而不是在主线程内完成它,从而保证用户交互过程的流畅。

Android API版本中广播机制重要改变

  • Android 5.0开始粘滞广播和有序粘滞广播过期,以后不再建议使用;
  • Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。

在这里重点讲一下Intent中flag新增的两个参数:

从Android 3.1开始,系统的软件包管理器跟踪处于停止状态(stopped state)的应用程序,提供了一种控制其启动后台进程和其他应用程序方式。

需要注意的是应用程序的停止状态(stopped state)和Activity的停止状态是不一样的。该系统可以分别管理这两种停止状态。

该平台定义了两个新的Intent的Flag,让发送者指定的意图是否应该被允许激活停止的应用程序的组件。当两个Flag都不设置或都设置的时候,默认操作是FLAG_INCLUDE_STOPPED_PACKAGES。

请注意,系统向所有的Intent的广播添加了FLAG_EXCLUDE_STOPPED_PACKAGES标志。它这样做是为了防止广播无意中的或不必要地开启组件的stoppped应用程序的后台服务。后台服务或应用程序可以通过向广播Intent添加FLAG_INCLUDE_STOPPED_PACKAGES标志来唤醒处于停止状态(stopped state)的应用程序。

应用程序处于停止状态情况有两种:

  1. 第一次安装,但尚未启动
  2. 在管理应用程序中由用户手动停止

简单的说,就是防止开机启动恶意程序,优化启动。经过验证发现,系统级的应用程序是可以接收到开机启动广播的。

观察者模式

事实上在Android系统中广播的实现机制是一种观察者模式,只要发送一个广播,所有已经注册过的广播接收者都可以接收到广播,下面用一个简单的示例来模拟一下广播的实现机制:

定义一个基类叫BroadcastReceiver,里面含有一个抽象方法,让所有继承该类的实现类都实现的一个广播接收的方法onReceiver.

定义一个Activity,实现相应的注册、注销以及发送广播的方法

接下来我们定义MyReceiver01、MyReceiver02和MyReceiver03分别继承BroadcastReceiver,实现onReceiver方法,新建一个测试类MyActivity继承Activity。

这就是一个观察者模式实现的广播收发的最简单的实现,当然了还可以加入一些参数,如果要实现有序广播,我们可以在BroadcastReceiver中添加一个排序字段,当我们要执行Activity中发送广播的sendBroadcast方法时,可以按照一定规则对排序字段排序然后按顺序执行广播接收者,这样就可以发送一个有序广播了。当然了如果还想再复杂就可以在BroadcastReceiver加入一个JavaBean,在JavaBean中加入更多属性字段,分别实现相应的类似IntentFilter的规则,然后在执行sendBroadcast方法时依据特定的规则执行。

小结

本文讲到这里就算结束了,都是一些基础的知识点,示例源代码下载请点击这里,示例中包含了电量、开机启动、网络连接和短信等相应的广播处理方式。由于自己水平有限,如有疏漏之处还请指正以求共同进步。

本文地址www.sunnyang.com/355.html

示例源代码下载

参考资料

Android总结篇系列:Android广播机制

Android 4.0及以上版本接收开机广播BOOT_COMPLETED、开机自启动服务

android 发送有序广播sendOrderBraodCast、截断广播和广播之间的数据传递

Android 中的BroadCastReceiver

使用LocalBroadcastManager

android广播机制

Android 4.0 无法接收开机广播的问题

示例源代码下载

LocalBroadcastManager 的实现原理,还是 Binder?

发表评论