LocalBroadcastManager—创建更高效、更安全的广播
2015-11-10 17:45
495 查看
前言
在写Android应用时候,有时候或多或少的需要运用广播来解决某些需求,我们知道广播有一个特性,就是使用sendBroadcast(intent);发送广播时,手机内所有注册了
BroadcastReceiver的应用都可以在接收到这个广播的,并在
BroadcastReceiver的
onReceive()方法进行匹配,而应用是否使用这个广播则是取决与我们定义的Action与广播接收者的是否匹配,也就是说平常我们使用的广播是全局的广播,谁都有权收到。所以这就有可能产生安全漏洞和隐私数据泄密:
1、假如别人反编译你的apk后知道了你的Action,那么第三方应用就可以发送与该Action匹配的广播,而你的应用也可以接收到,所以这就有可能被第三方利用这个来搞一些事
2、同样假如别人知道了你应用内的Action,当你使用广播来传输一些数据的时候,而其它应用也能接受到这个广播,通过Action匹配,就有可能获取到你的私密数据
通过上面分析,就说明了全局广播有两个地方需要注意,一是你的应用可以接收任何应用发出的广播,二是你的应用发出的广播可以被任何应用收到。
而通常来说我们使用广播大多数只是让它在自己的应用内传播,所以全局广播并不适合应用在此场景,而对这个问题,Google出了一个LocalBroadcastManager用来创建更高效、更安全的本地广播。
LocalBroadcastManager介绍
LocalBroadcastManager从名字上看就知道这个发送的广播只在本应用内传播,官方是这么介绍LocalBroadcastManager的:使用它发送的广播将只在自身App内传播,因此你不必担心泄漏隐私数据
其它App无法对你的App发送该广播,因为你的App根本就不可能接收到非自身应用发送的该广播,因此你不必担心有安全漏洞可以利用
比系统的全局广播更加高效
LocalBroadcastManager使用
使用LocalBroadcastManager和平常使用系统的全局广播差不多,都得先注册然后再发送广播,最后取消注册。注册广播
LocalBroadcastManager内部实现是一个单例模式(它的内部原理稍后讲),现在只先看看怎么使用LocalBroadcastManager,首先看看LocalBroadcastManager这个类拥有哪些方法:可以看到只有我圈起来的方法才对外开放的,无非就是getInstance,注册,发送,取消注册这四个方法
先来看看注册这个方法:
LocalBroadcastManager.getInstance(Context context).registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
我们需要传入的有两个很重要的参数,一是广播接收器BroadcastReceiver,二是IntentFilter对象,这个IntentFilter是我们为这个广播接收器指定的
发送广播
LocalBroadcastManager.getInstance(Context context).sendBroadcast(Intent intent);
发送广播只需要我们传入一个Intent即可
取消注册
LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver receiver);
取消注册只需要传入我们需要取消的广播接收器
代码实现
下面就看看使用LocalBroadcastManager的示例代码:public class MainActivity extends AppCompatActivity { private static final String LOCAL_ACTION = "com.sunzxy.demo.BROADCAST_ACTION"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //注册 LocalBroadcastManager.getInstance(this).registerReceiver(new MyMyBroadcastReceiver(),new IntentFilter(LOCAL_ACTION)); } public void click(View view){ //发送广播 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(LOCAL_ACTION)); } @Override protected void onDestroy() { super.onDestroy(); //取消注册 LocalBroadcastManager.getInstance(this).unregisterReceiver(new MyMyBroadcastReceiver()); } final class MyMyBroadcastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { if(intent!=null && LOCAL_ACTION.equals(intent.getAction())){ Log.v("zxy",intent.getStringExtra("name")); } } } }
LocalBroadcastManager内部实现原理
同样,既然官方说了它更高效、发送的广播只会在本应用内传播从而保证了安全性,那么它内部是怎么实现的呢?还是从LocalBroadcastManager的结构看起
我们从中可以看到比较重要的部分:
1、它内部有两个内部类,分别为ReceiverRecord和BroadcastRecord
2、它含有三个集合来管理
3、它内部有一个Handler对象
好了,知道了大概结构,那么接下来就是一一取出源码来分析了。
首先先看看LocalBroadcastManager的构造函数:
private static LocalBroadcastManager mInstance; private final Handler mHandler; public static LocalBroadcastManager getInstance(Context context) { synchronized (mLock) { if (mInstance == null) { mInstance = new LocalBroadcastManager(context.getApplicationContext()); } return mInstance; } } private LocalBroadcastManager(Context context) { mAppContext = context; mHandler = new Handler(context.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_EXEC_PENDING_BROADCASTS: executePendingBroadcasts(); break; default: super.handleMessage(msg); } } }; }
可以看到它内部是由单例实现的而私有化构造函数,而构造函数里创建了一个Handler对象,Handler对象传入的Looper是MainLooper对象,意为这个Handler的工作线程是主线程,而在看executePendingBroadcasts()这个方法之前,我们先看看它内部的三个集合的作用:
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>(); private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<String, ArrayList<ReceiverRecord>>(); private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<BroadcastRecord>();
这又涉及到两个内部类BroadcastRecord和ReceiverRecord,从名字上看就知道它们分别是广播记录实体类和接收器记录实体类:
private static class ReceiverRecord { final IntentFilter filter; final BroadcastReceiver receiver; boolean broadcasting; ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) { filter = _filter; receiver = _receiver; } @Override public String toString() { StringBuilder builder = new StringBuilder(128); builder.append("Receiver{"); builder.append(receiver); builder.append(" filter="); builder.append(filter); builder.append("}"); return builder.toString(); } } private static class BroadcastRecord { final Intent intent; final ArrayList<ReceiverRecord> receivers; BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) { intent = _intent; receivers = _receivers; } }
从这两个实体类的源码上看,ReceiverRecord是记录广播接收器(BroadcastReceiver)和它对应的过滤规则(IntentFilter),BroadcastRecord则是记录与发送的Intent匹配的ReceiverRecord的集合,因为一个Intent在它们对应的Action规则是一样的情况下它可以同时被多个广播接收器(BroadcastReceiver)接收。
回到那三个集合:
1、mReceivers:是一个HashMap类型的集合,
BroadcastReceiver作为key,而它的value则是一个
ArrayList<IntentFilter>集合,为什么是一个集合呢?因为一个广播接收器可以接收不同的Action的广播,这样看来它的功能很明显,就是维护着一张映射表,即维护着广播接收器(BroadcastReceiver)所对应的所有的过滤规则(IntentFilter)映射表,如下图:
2、mActions:也是一个HashMap类型的集合,它的key是一个Action,value则是一个
ArrayList<ReceiverRecord>对象,同样它也维护着一张映射表,即维护着Action所对应的所有ReceiverRecord对象映射表,其实说白了,就是可以接收该Action的所有广播接收器的映射,这个图恰好和上面的相反:
3、mPendingBroadcasts:这是一个List集合,存储的是
BroadcastRecord对象,而
BroadcastRecord对象又是存储着一个Intent和
ReceiverRecord集合,所以
mPendingBroadcasts这个集合的作用就是用来存储与发送的广播的Action匹配的
ReceiverRecord集合,而在执行处理广播的时候将会遍历这个集合对这里面的广播接收器一一进行广播的接收,说白了就是一个存储广播接收器的存储器(而这个广播接收器的IntentFilter必须和发送广播时的Intent匹配)。
好了,了解了上面的东西,现在我们再来看看注册广播方法的源码:
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { synchronized (mReceivers) { //首先就是创建一个ReceiverRecord对象,传入的是我们的广播接收器和这个广播接收器的filter ReceiverRecord entry = new ReceiverRecord(filter, receiver); //得到这个广播接收器的IntentFilter过滤规则集合 ArrayList<IntentFilter> filters = mReceivers.get(receiver); if (filters == null) { //如果不存在的话则创建一个 filters = new ArrayList<IntentFilter>(1); mReceivers.put(receiver, filters); } //把这个filter add进广播接收器对应的规则集合中去 filters.add(filter); for (int i=0; i<filter.countActions(); i++) { //然后遍历这个filter得到它的Action String action = filter.getAction(i); ArrayList<ReceiverRecord> entries = mActions.get(action); if (entries == null) { entries = new ArrayList<ReceiverRecord>(1); mActions.put(action, entries); } //通过遍历得到Action后,一一分别对每个Action建立Action与ArrayList<ReceiverRecord>对应的映射表 entries.add(entry); } } }
总结来说,registerReceiver()方法就做了两件事:
1、为传入的广播接收器添加指定的IntentFilter过滤规则
2、把IntentFilter里面的所有Action分别建立对
ArrayList<ReceiverRecord>的映射,也就是为相应的Action添加广播接收器,表示这个广播接收器可以接收此Action的广播
再看unregisterReceiver()方法:
public void unregisterReceiver(BroadcastReceiver receiver) { synchronized (mReceivers) { //在mReceivers表中移除key为receiver的对象 ArrayList<IntentFilter> filters = mReceivers.remove(receiver); if (filters == null) { return; } //取出这个广播接收器的filter中含有的所有action,然后在mActions表中,分别得到这个action对应的广播接收器集合,再判断是否含有我们需要移除的receiver,如果有则移除 for (int i=0; i<filters.size(); i++) { IntentFilter filter = filters.get(i); for (int j=0; j<filter.countActions(); j++) { String action = filter.getAction(j); ArrayList<ReceiverRecord> receivers = mActions.get(action); if (receivers != null) { for (int k=0; k<receivers.size(); k++) { if (receivers.get(k).receiver == receiver) { receivers.remove(k); k--; } } if (receivers.size() <= 0) { mActions.remove(action); } } } } } }
所以,unregisterReceiver()这个方法也做了两件事:
1、移除mReceivers表中广播接收器
2、移除mActions表中的广播接收器
再看看sendBroadcast()方法:
public boolean sendBroadcast(Intent intent) { synchronized (mReceivers) { final String action = intent.getAction(); final String type = intent.resolveTypeIfNeeded( mAppContext.getContentResolver()); final Uri data = intent.getData(); final String scheme = intent.getScheme(); final Set<String> categories = intent.getCategories(); final boolean debug = DEBUG || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); if (debug) Log.v( TAG, "Resolving type " + type + " scheme " + scheme + " of intent " + intent); //通过得到我们Intent的Action来获得该Action对应的所有的广播接收器的集合 ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction()); if (entries != null) { if (debug) Log.v(TAG, "Action list: " + entries); ArrayList<ReceiverRecord> receivers = null; //然后遍历该集合,进行匹配 for (int i=0; i<entries.size(); i++) { ReceiverRecord receiver = entries.get(i); if (debug) Log.v(TAG, "Matching against filter " + receiver.filter); if (receiver.broadcasting) { if (debug) { Log.v(TAG, " Filter's target already added"); } continue; } //当action,type,scheme,data,categories都完全相同时,这时匹配成功 int match = receiver.filter.match(action, type, scheme, data, categories, "LocalBroadcastManager"); if (match >= 0) { //匹配成功 if (debug) Log.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match)); if (receivers == null) { receivers = new ArrayList<ReceiverRecord>(); } //然后把匹配成功的ReceiverRecord对象添加到一个集合中去 receivers.add(receiver); receiver.broadcasting = true; } else { if (debug) { String reason; switch (match) { case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; case IntentFilter.NO_MATCH_DATA: reason = "data"; break; case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; default: reason = "unknown reason"; break; } Log.v(TAG, " Filter did not match: " + reason); } } } if (receivers != null) { for (int i=0; i<receivers.size(); i++) { receivers.get(i).broadcasting = false; } //最后再把存储匹配成功的ReceiverRecord对象集合添加到mPendingBroadcasts中去,而最终我们的广播接收是通过遍历mPendingBroadcasts这个集合来一一对这个集合里面的广播接收器进行广播的接收 mPendingBroadcasts.add(new BroadcastRecord(intent, receivers)); if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) { //这个非常重要,因为一开始我们就知道了在构造函数中会创建一个handler,而这个方法的名为sendBroadcast()其实不是真正的发送一个广播,而是通过handler来发送一个Message,然后在handlerMessage()回调方法中进行消息的处理,所以这也证实了这是一个本地广播,其它应用根本无法获取到,因为LocalBroadcastManager内部是通过Handler实现广播的发送的 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS); } return true; } } } return false; }
这个sendBroadcast()方法看起来比较长,其实一半的代码都是在做匹配,这段代码也做了三件事:
1、把我们发送的Intent的规则和mActions中对应action的
ArrayList<ReceiverRecord>集合里面的广播接收器的规则进行匹配,匹配成功则加入一个匹配成功的集合中
2、把匹配成功的集合加入到mPendingBroadcasts集合中
3、最重要的一点,它其实是通过handler发送一个Message来实现的
最后再来看看一开始提到的executePendingBroadcasts()方法,这个方法是在Handler中处理消息用的,我们在调用LocalBroadcastManager的sendBroadcast()方法时,它实际上是通过handler发送一个Message,然后在executePendingBroadcasts()方法中进行广播的接收:
private void executePendingBroadcasts() { while (true) { BroadcastRecord[] brs = null; synchronized (mReceivers) { final int N = mPendingBroadcasts.size(); if (N <= 0) { return; } brs = new BroadcastRecord ; //把mPendingBroadcasts集合转为数组 mPendingBroadcasts.toArray(brs); //然后清空mPendingBroadcasts集合 mPendingBroadcasts.clear(); } for (int i=0; i<brs.length; i++) { BroadcastRecord br = brs[i]; for (int j=0; j<br.receivers.size(); j++) { //然后通过循环遍历,得到mPendingBroadcasts集合中可以接收该广播的广播接收器进行广播的接收,通过调用onReceive()方法进行接收 br.receivers.get(j).receiver.onReceive(mAppContext, br.intent); } } } }
可以看到这个方法开了一个while循环进行轮询mPendingBroadcasts集合来进行广播的接收,处理完一遍后则会清空该集合,而下一次再有广播发送则重复这些动作。
LocalBroadcastManager总结
好了,LocalBroadcastManager的原理就分析完了,从中我们可以知道这么几点:LocalBroadcastManager高效的原因主要是因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和我们平时所用的一样,它的sendBroadcast()方法其实是通过handler发送一个Message实现的(当然这个方法还有规则匹配等作用)
既然是它内部是通过Handler来实现广播的发送的,那么相比与系统广播通过Binder实现那肯定是更高效了,同时使用Handler来实现,别的应用无法向我们的应用发送该广播,而我们应用内发送的广播也不会离开我们的应用
LocalBroadcastManager内部协作主要是靠这两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要就是存储待接收的广播对象
假如我们需要让一个广播接收器能接收多个不同Action的广播,我们可以在注册时候通过IntentFilter为该广播添加多条Action规则,这样只要符合其中一条该广播接收器就可以接收到了,如下:
//注册 IntentFilter filter = new IntentFilter(); filter.addAction("com.sunzxy.demo.BROADCAST_ACTION_1"); filter.addAction("com.sunzxy.demo.BROADCAST_ACTION_2"); filter.addAction("com.sunzxy.demo.BROADCAST_ACTION_3"); LocalBroadcastManager.getInstance(this).registerReceiver(new MyMyBroadcastReceiver(),filter);
相关文章推荐
- Nginx实战之让用户通过用户名密码认证访问web站点
- Linux 本地yum源搭建和网络yum源搭建
- 九度oj-1483-求最大最小数
- 【leetcode】Maximum Subarray
- 03(maven+SSH)之数据库设计(PMD)
- Java中subList的问题
- 离职谈话
- Node.js、express、mongodb 实现分页查询、条件搜索
- Javascript获取最近若干个月
- 04(maven+SSH)之maven热部署
- Spring注解详解
- 访问内容提供者
- HttpUtils注释
- 黑盒测试与白盒测试的用处
- java实现定时任务的三种方法
- android 6.0编译
- MongoDB基础CRUD JAVA语句
- linux系统双网卡绑定单个IP地址
- Http请求方式和框架
- UVA-10285 Longest Run on a Snowboard (递推)