在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。所以非常有必要详细学习一下 Broadcast 的使用。
Broadcast 类型
普通广播
完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播。
有序广播
按照接收者声明的优先级别(声明在<intent-filter>元素的<android:priority>属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置),被接收者依次接收广播。如:A的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后传给C。A得到广播后,可以往广播里存入数据,当广播传给B时,B可以从广播中得到A存入的数据。
Broadcast 注册方式
无论是什么注册方式,都要创建实现BroadcastReceiver 类并实现 onReceive 方法:
public class MyBroadcast extends BroadcastReceiver{
private static final String TAG = "MyBroadcast";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"broadcast msg ----->" + intent.getStringExtra("name"));
Log.d(TAG,"broadcastReceiver ----->");
}
}
Broadcast 静态注册
隐式注册只要在 AndroidManifest.xml 中用<receiver>标签注册,并在标签内用<intent-filter>标签设置过滤器。
注册代码如下:
<receiver android:name=".broadcast.MyBroadcast">
<intent-filter>
<action android:name="com.cfox.boradcast"/>
</intent-filter>
</receiver>
发送广播代码:
Intent intent = new Intent();
intent.putExtra("name","my broadcast");
intent.setAction("com.cfox.boradcast");
sendBroadcast(intent);
Broadcast 动态注册
定义并设置好一个 IntentFilter 对象,然后在需要注册的地方调Context.registerReceiver()方法,如果取消时就调用 Context.unregisterReceiver()方法。如果用动态方式注册的BroadcastReceiver的Context对象被销毁时,BroadcastReceiver也就自动取消注册了。
注册代码如下
IntentFilter filter = new IntentFilter();
filter.addAction("com.cfox.boradcastFilter");
MyBroadcast broadcast = new MyBroadcast();
registerReceiver(broadcast,filter);
发送代码如下
Intent intent = new Intent();
intent.putExtra("name","my broadcast");
intent.setAction("com.cfox.boradcastFilter");
sendBroadcast(intent);
普通广播
上面介绍的都是基于普通广播的,在这里就不多说了,重新贴一下上面的代码,复习一下吧。
注册代码如下:
<receiver android:name=".broadcast.MyBroadcast">
<intent-filter>
<action android:name="com.cfox.boradcast"/>
</intent-filter>
</receiver>
发送广播代码:
Intent intent = new Intent();
intent.putExtra("name","my broadcast");
intent.setAction("com.cfox.boradcast");
sendBroadcast(intent);
有序广播的使用
使用有序广播,我们要对其设置优先级,上面有说,所以在这里不再介绍。
注册代码(静态):
<receiver android:name=".broadcast.MyBroadcastOne" >
<intent-filter android:priority="1000">
<action android:name="com.cfox.broadcast"/>
</intent-filter>
</receiver>
<receiver android:name=".broadcast.MyBroadcastTow" >
<intent-filter android:priority="999">
<action android:name="com.cfox.broadcast"/>
</intent-filter>
</receiver>
注册代码(动态):
IntentFilter filter1 = new IntentFilter();
filter1.addAction("com.cfox.broadcast");
filter1.setPriority(1000);
MyBroadcastOne broadcastOne = new MyBroadcastOne();
registerReceiver(broadcastOne,filter1);
IntentFilter filter2 = new IntentFilter();
filter2.addAction("com.cfox.broadcast");
filter2.setPriority(999);
MyBroadcastTow broadcastTwo = new MyBroadcastTow();
registerReceiver(broadcastTwo,filter2);
发送有序广播
首先我们要先看一下,发送有序广播的方法:
sendOrderedBroadcast(
Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,
Handler scheduler, int initialCode, String initialData,
Bundle initialExtras)
参数很多,下面我们来介绍一下:
- 参数1:Intent
- 参数2:设置broadcast 权限
- 参数3:这是一个广播(broadcastReceiver),改广播在有序广播结束后调用该广播,如果有序广播在执行中被终止了,则这个广播将不会被执行。
- 参数4:Handler,可以为null,如果为 null ,将会使用主线程中的 Context
- 参数5:结果码,经常Activity.RESULT_OK
- 参数6:初始数据,可以在 onReceive 中使用 getResultData() 方法获取。通常为null.
- 参数7:Bundle , 经常为null
下面我们看看如何发送一个有序广播:
Intent intent = new Intent();
intent.putExtra("name","my broadcast");
intent.setAction("com.cfox.broadcast");
sendOrderedBroadcast(intent,null,new Receiver(),null,0,"hello good",null);
代码不多,也不必解释,下面把继承 BroadcastReceiver 类贴一下:
- MyBroadcastOne.class
public class MyBroadcastOne extends BroadcastReceiver {
private static final String TAG = "MyBroadcastOne";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"MyBroadcastOne msg ----->" + intent.getStringExtra("name"));
Log.d(TAG,"MyBroadcastOne ----->");
Log.d(TAG,"receiver--isOrderedBroadcast-->" + isOrderedBroadcast());
if (intent.getStringExtra("name").equals("my broadcast")){
Log.d(TAG,"ResultData msg ----->" + getResultData());
setResultData(getResultData() + " good good");
// abortBroadcast();// 终止广播
}
}
}
- MyBroadcastTow.class
public class MyBroadcastTow extends BroadcastReceiver{
private static final String TAG = "MyBroadcastTow";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"MyBroadcastTow msg ----->" + intent.getStringExtra("name"));
Log.d(TAG,"MyBroadcastTow ----->");
Log.d(TAG,"ResultData msg ----->" + getResultData());
Log.d(TAG,"receiver--isOrderedBroadcast-->" + isOrderedBroadcast());
}
}
- Receiver
public class Receiver extends BroadcastReceiver {
private static final String TAG = "Receiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"ResultData msg ----------->" + getResultData());
Log.d(TAG,"receiver--isOrderedBroadcast-->" + isOrderedBroadcast());
}
}
运行结果
D/MyBroadcastOne: MyBroadcastOne msg ----->my broadcast
D/MyBroadcastOne: MyBroadcastOne ----->
D/MyBroadcastOne: receiver--isOrderedBroadcast-->true
D/MyBroadcastOne: ResultData msg ----->hello good
D/MyBroadcastTow: MyBroadcastTow msg ----->my broadcast
D/MyBroadcastTow: MyBroadcastTow ----->
D/MyBroadcastTow: ResultData msg ----->hello good good good
D/MyBroadcastTow: receiver--isOrderedBroadcast-->true
D/Receiver: ResultData msg ----------------------->hello good good good
D/Receiver: receiver--isOrderedBroadcast-->false
看到运行结果,很多东西就都清楚了。下面再简单介绍一下。
终止有序广播
在有序广播中onReceive 方法中适当的位置调用 abortBroadcast() 方法终止广播。
一些方法介绍
- isOrderedBroadcast()
判断该 BroadcastReceiver 是不是有序广播。
- getResultData()
获取 BroadcastReceiver 间传递数据,这个数据可以在发送有序广播时通过第六个参数进行初始化设置。
使用 LocalBroadcastManager 动态注册
IntentFilter filter1 = new IntentFilter();
filter1.addAction("com.cfox.broadcast");
filter1.setPriority(1000);
MyBroadcastOne broadcastOne = new MyBroadcastOne();
IntentFilter filter2 = new IntentFilter();
filter2.addAction("com.cfox.broadcast");
filter2.setPriority(999);
MyBroadcastTow broadcastTwo = new MyBroadcastTow();
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastOne,filter1);
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastTwo,filter2);
不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型
以下内容参考自:http://blog.csdn.net/oonullpointeralex/article/details/48015107
- 对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext;
- 对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context;
- 对于通过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context。
- 注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。
使用注意
静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立.
Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。
- 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:对于动态注册类型的BroadcastReceiver,由于此注册和取消注册实在其他组件(如Activity)中进行,因此,不受此改变影响。
注2:在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以此进行一些业务上的处理(如即时app已经退出,仍然能接收到,可以启动service等..),3.1后,静态注册接受广播方式的改变,将直接导致此类方案不再可行。于是,通过将Service与App本身设置成不同的进程已经成为实现此类需求的可行替代方案。
BroadcastReceiver中不要执行耗时操作
BroadcastReceiver的生命周期比较短,一些比较费时的操作不应该放在onReceiver里完成。如果在onReceiver()的方法不能在10秒内执行完成,将会产生程序无响应也就是我们熟悉的ANR(Application not Response)。
广播接收者的对象只有在回调onReceive()这个函数时有效,一旦从这个函数返回,这个对象就被结束,不再激活。在onReceive()中,任何异步的操作都是不可行的。因为需要从onRecive()这个函数回来去处理异步的操作,但是这个广播接收者不再被激活,系统将会在异步操作完成前结束进程。