Android四大组件详解---BroadcastReceicer广播接收者
广播有两个角色,一个是广播发送者,另一个是广播接收者。
广播按照类型分为两种,一种是全局广播,另一种是本地广播
全局广播:就是发出的广播被其他任意应用程序接收,或者可以接收来自其他任意应用程序的广播。
本地广播:则是只能在应用程序内部进行传递的广播,广播接收器也只能接收内部的广播,不能接收其他应用程序的广播
广播按照机制分两种,一种是标准广播,一种是有序广播
标准广播:是一种异步的方式来进行传播的,所有接收者都会接收事件,不可以被拦截,不可以被修改
有序广播:是一种同步执行的广播,按照优先级,一级一级的向下传递,接收者可以修改广播数据,也可以终止广播事件。
一:使用广播接收器接收广播
1.定义一个TestReceiver类继承广播接收者BroadcastReceiver,复写其中的onReceive()方法
public class TestReceiver extends BroadcastReceiver {
private static final String TAG="TestReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"onReceive()");
}
}
2.对广播进行注册
注册方式有两种,一种是动态注册,一种是静态注册
动态注册
public class ReceActivity extends AppCompatActivity {
private TestReceiver receiver;
private IntentFilter intentFilter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rece);
receiver=new TestReceiver();
intentFilter=new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//当网络发生变化的时候,系统广播会发出值为android.net.conn.CONNECTIVITY_CHANGE这样的一条广播
registerReceiver(receiver,intentFilter);
}
}
结果:切换网络变化
D/TestReceiver: onReceive()
静态注册,像这种系统广播,android8.0以上对静态广播注册做了严格限制,就没法接受到网络变化的广播
静态注册一个自定义的广播。
//定义action为“11”
//方式1
Intent intent=new Intent("11");
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
intent.setPackage(getPackageName());//包名发送广播com.soudao.test
}
sendBroadcast(intent);
//方式2
Intent intent=new Intent("11");
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
//该方式适用:给其他应用的广播接收者发送消息(指定应用的包名、指定类的全类名)
intent.setComponent(new ComponentName(getPackageName(), getPackageName()+".TestReceiver"));
intent.setClassName(getPackageName(), getPackageName()+".TestReceiver");
}
sendBroadcast(intent);
结果:
都调用到onReceive()
D/TestReceiver: onReceive()
动态自定义广播
public class ReceActivity extends AppCompatActivity {
private TestReceiver receiver;
private IntentFilter intentFilter;
private Button btn_b;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rece);
//动态注册广播
receiver=new TestReceiver();
intentFilter=new IntentFilter();
intentFilter.addAction("11");
registerReceiver(receiver,intentFilter);//注册广播
Log.d("aa",getPackageName());
//点击按钮发送广播
btn_b=findViewById(R.id.btn_b);
btn_b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent("11");
sendBroadcast(intent);//发送广播,这是一个无序的广播
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);//解除广播注册
}
}
动态发送有序广播,设置了优先级Priority属性(-1000-1000)
//动态注册一个广播接收者TestReceiver
receiver=new TestReceiver();
intentFilter=new IntentFilter();
intentFilter.addAction("11");
intentFilter.setPriority(200);
registerReceiver(receiver,intentFilter);
//动态注册一个广播接收者TestReceiver2
receiver2=new TestReceiver2();
intentFilter=new IntentFilter();
intentFilter.addAction("11");
intentFilter.setPriority(100);
registerReceiver(receiver2,intentFilter);
public class TestReceiver2 extends BroadcastReceiver {
private static final String TAG="TestReceiver2";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"我是TestReceiver2的onReceive()");
}
点击按钮发送广播
Intent intent=new Intent("11");
sendOrderedBroadcast(intent,null);
结果:
D/TestReceiver: onReceive()
D/TestReceiver2: 我是TestReceiver2的onReceive()
如果我们想拦截广播
可以在onReceive()中调用abortBroadcast();即广播就不会再传递下去了
本地广播使用
业界常见的一些增加安全性的方案包括:
1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收
2.在广播发送和接收时,都增加相应的permission,用于权限验证
3.发送广播时,指定特定广播所在的包名,具体是通过intent.setPackage(packageName)指定,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
4.采用LocalBroadcastManager的方式
本地广播LocalBroadcastManager使用该机制发出的广播只能够在应用程序内部进行传递,并且广播接收器也只能接收来自本地应用程序发出的广播,这样所有的安全性问题都不存在了。
public class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("LocalReceiver","我是本地广播接收器");
}
}
//注册本地广播
LocalBroadcastManager localBroadcastManager=LocalBroadcastManager.getInstance(this);//获取实例
LocalReceiver localReceiver=new LocalReceiver();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("cccc");
localBroadcastManager.registerReceiver(localReceiver,intentFilter);//注册本地广播监听
@Override
protected void onDestroy() {
super.onDestroy();
//unregisterReceiver(receiver);
localBroadcastManager.unregisterReceiver(localReceiver);//注销本地广播
}
发送本地广播
Intent intent=new Intent("cccc");
localBroadcastManager.sendBroadcast(intent);//发送本地广播
//localBroadcastManager.sendBroadcastSync(intent);//发送本地有序广播
结果:
D/LocalReceiver: 我是本地广播接收器
广播权限有关
静态注册的方式:
自定义广播权限
//permission的目录和同级的位置配置使用到的权限
//protectionLevel="signature"的属性有哪些:
//normal:默认的,应用安装前,用户可以看到相应的权限,但无需用户主动授权。
//dangerous:normal安全级别控制以外的任何危险操作。需要dangerous级别权限时,Android会明确要求用户进行授权。常见的如:网络使用权限,相机使用权限及联系人信息使用权限等。
//signature:它要求权限声明应用和权限使用应用使用相同的keystore进行签名。如果使用同一keystore,则该权限由系统授予,否则系统会拒绝。并且权限授予时,不会通知用户。它常用于应用内部。**把protectionLevel声明为signature。如果别的应用使用的不是同一个签名文件,就没办法使用该权限,从而保护了自己的接收者**
//添加了一个自定义的权限
动态注册权限
receiver=new TestReceiver();
intentFilter=new IntentFilter();
intentFilter.addAction("11");
//intentFilter.setPriority(200);
registerReceiver(receiver,intentFilter,"com.ruan.rocky.permission",null);
在注册的时候,最关键的一点是用registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)进行注册,而不是平常用的是registerReceiver(BroadcastReceiver, IntentFilter)。相较于后者,前者在注册的时候要求了发送者必须具有的权限。如果发送者没有该权限,那么发送者发送的广播即使经过IntentFilter的过滤,也不会被receiver接收。此时如果再自定义一个权限,并且将权限的protectionLevel设置为signature,那么外部应用便无法使用该权限,也就无法触及到该receiver。
发送广播:
Intent intent=new Intent("11");
sendBroadcast(intent,"com.ruan.rocky.permission");//发送无序的权限 广播
//sendOrderedBroadcast(intent,"com.ruan.rocky.permission");发送有序的权限,广播
Android广播机制
1.同一app内部的同一组件内的消息通信(单个或多个线程之间);
2.同一app内部的不同组件之间的消息通信(单个进程);
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信
从实现原理上,Android中的广播采用了观察者模式,基于消息的发布/订阅事件模型,因此,从实现的角度来看,Android中的广播将广播的发送者和接收者极大程度的解耦,使得系统能方便集成,更易扩展
1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
2.广播发送者通过binder机制向AMS发送广播;
3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法
由此看来,广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。显然,整体流程与EventBus非常类似。
在上文说列举的广播机制具体可以使用的场景中,现分析实际应用中的适用性:
第一种情形:同一app内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若适用广播机制,显然有些“杀鸡牛刀”的感觉,会显太“重”;
第二种情形:同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对统一进程,用于处理此类需求非常适合,且轻松解耦。
第三、四、五情形:由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜
结尾:拨开云雾见天日 守得云开见月明