Android探索之BroadcastReceiver具体使用以及安全性探究
前言:
最近的计划是学习一下iOS的NSNotificationCenter,突然想起来的Android的广播机制,所以还是觉得先对BroadcastReceiver来个全面的总结然后再去学习NSNotificationCenter。
BroadcastReceiver简介:
BroadcastReceiver是Android四大组件之一,广播是一种广泛运用的在应用程序之间传输信息的机制,而BroadcastReceiver 是对发送出来的广播进行过滤接收并响应的一类组件;广播接收者( BroadcastReceiver )用于接收广播 Intent ,广播 Intent 的发送是通过调用 Context.sendBroadcast() 、 Context.sendOrderedBroadcast() 来实现的。通常一个广播 Intent 可以被订阅了此 Intent 的多个广播接收者所接收。
广播的使用场景:
1.同一app内部的同一组件内的消息通信(单个或多个线程之间);
2.同一app内部的不同组件之间的消息通信(单个进程);
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信。
广播的分类:
普通广播:
发送方式:Context.sendBroadcast()
优点:完全异步,消息传递效率高,
缺点:不能处理广播传给一个接收者,不能终止广播的传播
有序广播:
发送方式:Context.sendOrderedBroadcast()
优点:可以根据广播接收者的优先级依次传播,广播接收者可以处理广播然后再传给一下广播接收者,也可以根据需要调用abortBroadcast()终止广播传播。
缺点:效率低
广播的使用方式:
动态注册:
//注册广播 private void registerReceiver(){ IntentFilter dynamicFilter = new IntentFilter(); dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//添加动态广博Action registerReceiver(dynamicReceiver, dynamicFilter); } //解除注册 private void unRegisterReceiver() { unregisterReceiver(dynamicReceiver); } //动态广播的Receiver private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(ActionCodes.DYNAMICACTION)){ //动作检测 String msg = intent.getStringExtra("msg"); String finalMsg= String.format("%s%s","CActivity----->收到广播:",msg); Log.e("dynamicReceiver",finalMsg); Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show(); } } };
一般情况在Activity/Fragment 的onCreate/onStart/onResume 中注册, 在onDestory/onStop/onPause 中解除注册,根据不同的需求选择不能的生命周期函数。
静态注册:
<receiver android:name=".StaticReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
发送广播:
//发送普通广播 Intent intent = new Intent(); intent.setAction(ActionCodes.DYNAMICACTION);//设置Action intent.putExtra("msg", "我是普通广播(动态注册)");//添加附加信息 sendBroadcast(intent); //发送有序广播 Intent intent = new Intent(); intent.setAction(ActionCodes.DYNAMICACTION);//设置Action intent.setPackage(getPackageName());//设置包名 intent.putExtra("msg", "我是有序广播(动态注册)");//添加附加信息 sendOrderedBroadcast(intent,null);
以上基本上可以满足广播的基本使用了,接下来我们在写个测试程序:分别在A,B,C三个Activity中动态注册广播,分别发送普通广播和和有序广播。
发送普通广播接收顺序:
分别给A,B,C三个Activity中动态注册广播的优先级设置未100,500,1000接收顺序:
附上设置优先级方式:
IntentFilter dynamicFilter = new IntentFilter(); dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//添加动态广播的Action dynamicFilter.setPriority(1000);//设置优先级 registerReceiver(dynamicReceiver, dynamicFilter);
上文已知有序广播可以修改广播信息传递给下一级优先级低的接收者,我们让BActivity修改 让AActivity接收:
BActivity 广播实现
//动态广播的Receiver private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(ActionCodes.DYNAMICACTION)){ //动作检测 String msg = intent.getStringExtra("msg"); String finalMsg= String.format("%s%s","BActivity----->收到广播:",msg); Log.e("dynamicReceiver",finalMsg); Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show(); if(isOrderedBroadcast()) { //创建一个Bundle对象,并存入数据 Bundle bundle = new Bundle(); bundle.putString("msg", msg + "来自BActivity"); //将bundle放入结果中 setResultExtras(bundle); //取消Broadcast的继续发送 //abortBroadcast(); } } } };
AActivity 如何接收:
//动态广播的Receiver private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(ActionCodes.DYNAMICACTION)) { //动作检测 String msg = intent.getStringExtra("msg"); String finalMsg = String.format("%s%s", "AActivity----->收到广播:", msg); Log.e("dynamicReceiver", finalMsg); if (isOrderedBroadcast()) { Bundle bundle = getResultExtras(true);//接收来自上一级优先级较高的广播修改的信息 String from = bundle.getString("msg"); if (TextUtils.isEmpty(from)) { return; } Log.e("dynamicReceiver", String.format("%s%s", "AActivity----->收到广播:", from)); Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show(); } } } };
运行结果:
有序广播如何终止广播传播:
// 终止Broadcast的继续发送 abortBroadcast();
运行结果:
静态注册的广播上述测试运行结果一致,设置优先级方式不同而已:
<receiver android:name=".AStaticReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"><!--设置优先级--> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
接下来测试一下app应用之间发送广播,发送方式也是通过隐式Intent方式:
动态注册广播接收情况:
普通广播:
有序广播:
静态注册广播接收情况:
普通广播:
有序广播:
看了上述测试结果基本上和app内运行效果一模一样,所以按照上述那种注册方式和使用方式,一旦app被反编译之后有一定的安全隐患,如何安全的传输呢?
第一种方式:
静态注册广播可以设置:android:exported="false"
<receiver android:name=".AStaticReceiver" android:enabled="true" android:exported="false" <!--设置只能接收app内广播 --> > <intent-filter android:priority="100"><!--设置优先级--> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
第二种方式:通过设置发送的广播只能app内接收
Intent intent = new Intent(); intent.setAction(ActionCodes.DYNAMICACTION);//设置Action intent.setPackage(getPackageName());//设置包名使广播只能被包名的app内接收者接收 intent.putExtra("msg", "我是普通广播(动态注册)");//添加附加信息 sendBroadcast(intent);
第三种方式通过自定义权限:通过上述两种方式只能达到屏蔽外来广播以及广播只在app内传播,无法实现app之间安全发送广播
自定义权限:
<permission android:name="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver" /> <uses-permission android:name="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver"/>
动态注册:
IntentFilter dynamicFilter = new IntentFilter(); dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//添加动态广播的Action dynamicFilter.setPriority(500); //设置权限 registerReceiver(dynamicReceiver, dynamicFilter,ActionCodes.MYPERMISSION,null);
静态注册:
<receiver android:name=".BStaticReceiver" android:enabled="true" android:exported="true" <!--设置权限--> android:permission="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver"> <intent-filter android:priority="500"> <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" /> </intent-filter> </receiver>
发送广播:
//普通广播 sendBroadcast(intent,ActionCodes.MYPERMISSION); //有序广播 sendOrderedBroadcast(intent,ActionCodes.MYPERMISSION);
第四种方式:通过LocalBroadcastManager方式
注册:
LocalBroadcastManager.getInstance(getInstance()).registerReceiver(receiver, filter);
解除注册:
LocalBroadcastManager.getInstance(getInstance()).unregisterReceiver(receiver);
发送广播:
LocalBroadcastManager.getInstance(getInstance()).sendBroadcastSync(intent);
总结:
通过本文可以看出BroadcastReceiver使用方式虽然看似简单,想要实现比较完善的广播还是要费一番功夫的。