4.3 相关知识
相关知识
在Android中,Broadcast是四大组件之一,也是一种广泛运用在应用程序之间传输信息的机制,Android中发送的广播内容是一个Intent,这个Intent中可以携带我们要发送的数据。
Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。
广播作为Android组件间的通信方式,可以使用的场景如下:
- l 同一app内有多个进程的不同组件之间的消息通信。
- l 不同app之间的组件之间消息的通信。
从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。
广播被分为两种不同的类型:“无序广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”。无序广播是完全异步的,可以在同一时刻(逻辑上)被所有广播接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播;然而有序广播是按照接收者声明的优先级别(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置),被接收者依次接收广播。如:A的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后传给C。A得到广播后,可以往广播里存入数据,当广播传给B时,B可以从广播中得到A存入的数据。
无序广播使用Context.sendBroadcast()方法发送,所有订阅者都有机会获得并进行处理。
有序广播使用Context.sendOrderedBroadcast()发送,系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播,如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果存放进广播Intent,然后传给下一个接收者。
广播接收者(BroadcastReceiver)用于接收广播Intent,广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收。要实现一个广播接收者方法如下:
第一步:定义广播接收者,继承BroadcastReceiver,并重写onReceive()方法。
public class IncomingSMSReceiver extendsBroadcastReceiver { @Override public void onReceive(Contextcontext, Intentintent) { } } 第二步:订阅感兴趣的广播Intent,订阅方法有两种:
第一种:使用代码进行订阅(动态订阅)
IntentFilter filter = newIntentFilter("android.provider.Telephony.SMS_RECEIVED"); IncomingSMSReceiver receiver = newIncomingSMSReceiver(); registerReceiver(receiver, filter); 第二种:在AndroidManifest.xml文件中的<application>节点里进行订阅(静态订阅)
<receiver android:name=".IncomingSMSReceiver"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> 动态广播订阅和静态广播订阅的区别如下:
静态订阅广播又叫常驻型广播,当你的应用程序关闭了,如果有广播信息来,你写的广播接收器同样的能接受到,他的注册方式是在应用程序中的AndroidManifast.xml进行订阅的。
动态订阅广播又叫非常驻型广播,当应用程序结束了,广播自然就没有了,在activity中的onCreate或者onResume中订阅广播,同时你必须在onDestory或者onPause中取消广播订阅,不然会报异常。
在发送广播的时候需要注意的是动态注册的时候使用的是隐式intent方式的,所以在发送广播的时候需要使用隐式Intent去发送,不然是广播接收者是接收不到广播的。但是静态订阅的时候,因为在AndroidMainfest.xml中订阅的,所以在发送广播的时候使用显示Intent和隐式Intent都可以(当然这个只针对于自定义的广播接收者),所以以防万一,一般都采用隐式Intent去发送广播。
Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES(包含已经停止的包)和FLAG_EXCLUDE_STOPPED_PACKAGES(不包含已经停止的包)。自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。
由此,对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。
但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。
Intent intent = new Intent(); intent.setAction(BROADCAST_ACTION); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); intent.putExtra("name", "qqyumidi"); sendBroadcast(intent); 使用广播有两个步骤:
\1. 广播的接收者需要通过调用registerReceiver函数告诉系统,它对什么样的广播有兴趣,即指定IntentFilter,并且向系统注册广播接收器,即指定BroadcastReceiver:
IntentFilter counterActionFilter = new IntentFilter( CounterService.BROADCAST_COUNTER_ACTION); registerReceiver(counterActionReceiver, counterActionFilter); 这里,指定感兴趣的广播就是CounterService.BROADCAST_COUNTER_ACTION了,而指定的广播接收器就是counterActonReceiver,它是一个BroadcastReceiver类型的实例。
\2. 广播的发送者通过调用sendBroadcast函数来发送一个指定的广播,并且可以指定广播的相关参数。
Intent intent = new Intent(BROADCAST_COUNTER_ACTION); intent.putExtra(COUNTER_VALUE, counter); sendBroadcast(intent) ; 指定的广播为CounterService.BROADCAST_COUNTER_ACTION,并且附带的带参数当前的计数器值counter。调用了sendBroadcast函数之后,所有注册了CounterService.BROADCAST_COUNTER_ACTION广播的接收者便可以收到这个广播了。
在第1步中,广播的接收者把广播接收器注册到ActivityManagerService中;在第2步中,广播的发送者同样是把广播发送到ActivityManagerService中,由ActivityManagerService去查找注册了这个广播的接收者,然后把广播分发给它们。
在第2步的分发的过程,其实就是把这个广播转换成一个消息,然后放入到接收器所在的线程消息队列中去,最后就可以在消息循环中调用接收器的onReceive函数了。这里有一个要非常注意的地方是,由于ActivityManagerService把这个广播放进接收器所在的线程消息队列后,就返回了,它不关心这个消息什么时候会被处理,因此,对广播的处理是异步的,即调用sendBroadcast时,这个函数不会等待这个广播被处理完后才返回。
学员评价