1. Broadcast简介
Broadcast用到的场景是非常多的,比如说实现在同一个App内的通信;或者不同App之间的通信;还有在特定情况下的通信(开机需要给更新App的提示等)。
Broadcast的角色主要有一个广播发送者,和需要接受到消息的广播接收者。
广播发送者先将广播发送出去,然后由广播接收者进行接收,收到广播后,就可以在广播接收器中做一些操作了。值得注意的是广播接收器是运行在UI线程的,所以不能做一些耗时操作,一般是起一个Service之类的,否则就会出现ANR。
2. Broadcast原理
首先,Broadcast采用的设计模式为观察者模式。
在这个模型中,主要有三个角色:广播发送者,广播接收者,消息中心(ActivityManagerService)。
Broadcast的具体流程如下:
1. 广播接收者首先在消息中心通过Binder机制注册。
2. 广播发送者就开始通过Binder机制向消息中心发送消息。
3. 然后消息中心通过匹配IntentFilter、Permission,在消息中心注册的广播接收者查中找合适的广播接收者。
4. 匹配到合适的广播接收者后,消息中心就要把广播消息发送到广播接收者相应的消息循环队列中。
5. 最后,广播接收者再通过消息循环拿到这个消息,并回调onReceive()方法。
这样,Broadcast的发送和接收操作就完成了。
3. 广播接收器
关于广播接收器的注册,可以分为两种类型:动态注册和静态注册。
动态注册适合使用在场景灵活,或者是特定场景下使用,因为它可以随意的注册和反注册(不使用的广播要将他注销掉);
而静态注册因为不受任何组件的生命周期影响,所以适合使用在需要时刻监听的广播的场景中。
可以根据使用场景的不同去选择不同的注册方式,下面来看具体的实现方式。
静态注册
首先,写一个广播接收器,并实现它的方法;在实现的onReceive()方法中,就是接受到广播之后要实现的逻辑了。
public class StaticBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "This is StaticBroadcast", Toast.LENGTH_SHORT).show();
}
}
接着,在AndroidManifest.xml中静态注册下该广播接收器,intent-filter标签中就是填写一些匹配条件,这样可以指定广播接收器接收广播,这里的action就是需要匹配的字符串,只要与发送方的action匹配成功(字符串完全相同),广播接收器就可以接收到发送的广播了。
<receiver android:name=".StaticBroadcast">
<intent-filter>
<action android:name="INTENT.SUYICHEN.STATIC.BROADCAST"/>
</intent-filter>
</receiver>
最后就是发送广播,以触发这个广播接收器。
private static final String STATIC_BROADCAST = "INTENT.SUYICHEN.STATIC.BROADCAST";
public void BroadcastToStatic(View view) {
Intent intent = new Intent();
intent.setAction(STATIC_BROADCAST);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendBroadcast(intent);
}
动态注册
接下爱就是动态注册的广播了,跟静态广播一样,首先要写的也是广播接收器,就是定义一个BroadcastReceiver,我们在接收到的地方加一个吐司。
private BroadcastReceiver DynamicBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "This is DynamicBroadcast", Toast.LENGTH_SHORT).show();
}
};
必不可少的还有匹配这个意图。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("INTENT.SUYICHEN.DYNAMIC.BROADCAST");
好了,万事俱备,注册!
this.registerReceiver(DynamicBroadcastReceiver,intentFilter);
不能着急,动态注册与静态注册不同的地方还在于他可以反注册,这也是他的优势之一,一定要在一个合适的地方将反注册 添加上。
this.unregisterReceiver(DynamicBroadcastReceiver);
最后我们试着发送一个广播看看能否弹出吐司。
private static final String DYNAMIC_BROADCAST = "INTENT.SUYICHEN.DYNAMIC.BROADCAST";
public void BroadcastToDynamic(View view) {
Intent intent = new Intent();
intent.setAction(DYNAMIC_BROADCAST);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendBroadcast(intent);
}
4. 广播的发送
广播的接收只有静态和动态两种的分别,而广播的发送方根据发送方式的不同被分为几种类型:普通广播、系统广播、有序广播、应用内广播。
分类虽然多,相似性非常大,也可以说换汤不换药把,使用最多的依旧是最简单的普通广播,下面来一一介绍下各个发送方式。
普通广播
普通广播非常简单,使用广,可以说是码农必会了。细心的小伙伴可能已经发现,我在之前写的静态和动态接收器的时候用到的就是普通广播,比如前面的这个:
Intent intent = new Intent();
intent.setAction(STATIC_BROADCAST);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendBroadcast(intent);
系统广播
系统广播无非就是系统已经写好了的广播,他会在特定的情况下去发送,我们只需写一个关闭接收器,将action字符串匹配上,就可以实现接收。与普通广播一样,只不过写发送方代码的人不同而已。
有序广播
关于有序广播,我们一起来做一个实验,分别创建4个广播接收器,让他们来依次接收到广播,但是,我们在第三个广播接收器中调用 abortBroadcast() 方法来进行广播的拦截,看是否能拦住广播不往下传递,也就是说第四个广播接收器不到广播。
首先创建四个广播接收器,并在第三个接收器的时候abort住:
public class OrderedBroadcastOne extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
}
}
public class OrderedBroadcastTwo extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
}
}
public class OrderedBroadcastThree extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
abortBroadcast();//Intercept
}
}
public class OrderedBroadcastFour extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("OrderedBroadcast",this.getClass().getName()+"already received");
}
}
其中,在AndroidManifest中,有个priority属性是设置广播接收器的级别,这个值的取值范围是[-1000,1000],取值越大,优先级越高。这样就可以用他来控制四个广播的接收顺序了。
<receiver android:name=".OrderedBroadcastOne">
<intent-filter android:priority="1000">
<action android:name="INTENT.SUYICHEN.ORDERED"/>
</intent-filter>
</receiver>
<receiver android:name=".OrderedBroadcastTwo">
<intent-filter android:priority="500">
<action android:name="INTENT.SUYICHEN.ORDERED"/>
</intent-filter>
</receiver>
<receiver android:name=".OrderedBroadcastThree">
<intent-filter android:priority="0">
<action android:name="INTENT.SUYICHEN.ORDERED"/>
</intent-filter>
</receiver>
<receiver android:name=".OrderedBroadcastFour">
<intent-filter android:priority="-1000">
<action android:name="INTENT.SUYICHEN.ORDERED"/>
</intent-filter>
</receiver>
接下来就要发送一个有序广播,来验证一下结果。
public void BroadcastToOrdered(View view) {
Intent intent = new Intent();
intent.setAction(ORDERED_BROADCAST);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendOrderedBroadcast(intent,null);
}
打印如下:
打印显示广播依次接收到了,但到了第三个广播因为执行了abort(),广播就被吞掉,第四个广播自然不会接收到。
应用内广播
虽然都是广播,但应用内的广播和其他广播的具体实现确大不相同。
一般的广播的实现原理都是将广播注册在消息中心(ActivityManagerService)里,如果消息中心的工作很多,消息中心繁忙,那么最直观的现象就是导致我们接收广播的速度大幅下降。
如果是广播只会在自己应用(一个进程)内使用,那么完全可以使用LocalBroadcastManager实现,LocalBroadcastManager是采用的是Handler的消息机制来处理的广播,注册到系统中的是通过Binder机制实现的,所以速度要快许多。
接下来就简单介绍下具体使用把,原理实现等用一个篇幅来讲解。
首先创建一个自定义的广播接收器类。
public class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("LocalReceiver","LocalReceiver is accept");
}
}
然后,在需要注册的地方获取他。
LocalReceiver mLocalReceiver = new LocalReceiver();
紧接着,注册和注销这个广播。
private static final String LOCAL_BROADCAST = "INTENT.SUYICHEN.BROADCAST.LOCAL";
LocalBroadcastManager.getInstance(this).registerReceiver(mLocalReceiver,new IntentFilter(LOCAL_BROADCAST));
LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalReceiver);
最后就是发送一个广播,看能否接收到了。
public void LocalBroadcast(View view) {
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(LOCAL_BROADCAST));
}
不出所料,广播收到了。
5. 一个广播接收器对应多个广播
如果说需要一个广播接收器同时接收多个广播,然后根据接收到的广播而做不同的处理,那怎么办呢?
有几个对应的广播,然后对应的写上几个广播接收器,这种方法是可以实现的,如果需要接收的广播多了,也就是对应的Action多了,也难免让代码看起来臃肿,解决这个也是非常简单,只是用广播接收器里传来的intent获取到当前是接受到的哪个广播,然后做一下判断,根据不同的广播做不同的处理就可以了。
由于这个仅为补充,比较简单,就直接上代码了。
广播接收器:
public class DoubleActionBroadcast extends BroadcastReceiver {
private static final String DOUBLE_ACTION_ONE = "INTENT.SUYICHEN.DOUBLE.ACTION.ONE";
private static final String DOUBLE_ACTION_TWO = "INTENT.SUYICHEN.DOUBLE.ACTION.TWO";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(DOUBLE_ACTION_ONE)){
Log.e(this.getClass().getName(),"accept to"+DOUBLE_ACTION_ONE);
}
if(action.equals(DOUBLE_ACTION_TWO)){
Log.e(this.getClass().getName(),"accept to"+DOUBLE_ACTION_TWO);
}
}
}
注册广播接收器:
<receiver android:name=".DoubleActionBroadcast">
<intent-filter>
<action android:name="INTENT.SUYICHEN.DOUBLE.ACTION.ONE"/>
<action android:name="INTENT.SUYICHEN.DOUBLE.ACTION.TWO"/>
</intent-filter>
</receiver>
分别携带不同的Action,发送两个广播。
public void DoubleBroadcast1(View view) {
Intent intent = new Intent();
intent.setAction(DOUBLE_ACTION_ONE);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendBroadcast(intent);
}
public void DoubleBroadcast2(View view) {
Intent intent = new Intent();
intent.setAction(DOUBLE_ACTION_TWO);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendBroadcast(intent);
}