码迷,mamicode.com
首页 > 移动开发 > 详细

Android 源码系列之<三>从安全的角度深入理解BroadcastReceiver(下)

时间:2016-04-29 19:16:12      阅读:225      评论:0      收藏:0      [点我收藏+]

标签:

        转载请注明出处:http://blog.csdn.net/llew2011/article/details/51152723

        在上一篇文章中我们结合实验讲解了有关使用BroadcastReceiver存在的安全性问题并且给出了相应的解决方案,如果你还没有看过上篇文章请点击这里,最后一条的解决方案是采用官方v4包中的LocalBroadcastManager来解决的,官方介绍说这种方式不仅安全而且更高效,今天我们就从源码的角度来了解一下LocalBroadcastManager,如果你对它非常熟悉,可以跳过本文了(*^__^*) …

        分析源码之前,我们首先回顾一下LocalBroadcastManager的用法:

        1、定义广播接收器,代码如下:

public class LocalBroadcastReceiver extends BroadcastReceiver {

	public static final String LOCAL_CUSTOM_ACTION = "com.llew.seetao.customaction";
	
	public LocalBroadcastReceiver() {
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		Log.e(this.getClass().getSimpleName(), "current time is :" + System.currentTimeMillis());
	}
}
        2、注册广播接收器,代码如下:
private void registerBroadcastReceiver() {
	mBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);
	mLocalReceiver = new LocalBroadcastReceiver();
	IntentFilter filter = new IntentFilter(LocalBroadcastReceiver.LOCAL_CUSTOM_ACTION);
	mBroadcastManager.registerReceiver(mLocalReceiver, filter);
}
        3、发送一个广播,代码如下:
public void sendBroadcast(View v) {
	Intent intent = new Intent(LocalBroadcastReceiver.LOCAL_CUSTOM_ACTION);
	mBroadcastManager.sendBroadcast(intent);
}

        分析源码一般是从使用开始分析,在开始分析之前我们先大致了解一下LocalBroadcastManager中都定义了哪些属性吧,部分属性如下所示:

private final Context mAppContext;
// 缓存集合
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>();

private final Handler mHandler;

private static LocalBroadcastManager mInstance;
        主要定义了代表当前App运行环境的mAppContext属性,然后定义了三个集合类型的属性,既然是定义为集合类型肯定是用来装载数据的,具体装载什么类型的数据,看泛型定义的是什么类型就明确了,了解了相关属性后我们开始分析源码。

        首先mBroadcastManager的实例化是同过LocalBroadcastManager的全局静态方法getInstance()来实现的,该方法需要一个Context,我们直接传递当前的Context就好了。我们进入该方法查看一下是如何进行实例化的,代码如下:

public static LocalBroadcastManager getInstance(Context context) {
    synchronized (mLock) {
        if (mInstance == null) {
            mInstance = new LocalBroadcastManager(context.getApplicationContext());
        }
        return mInstance;
    }
}
        此方法很简单,通过单例模式来保证只有一个LocalBroadcastManager的实例,我们接着进入其构造方法中,看看里边都做了什么操作,代码如下:
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);
            }
        }
    };
}

        在构造方法中把传递进来的context赋值给了mAppContext,然后通过context的getMainLooper()方法初始化了mHandler变量(这种方式是基于主线程的Looper进行了初始化,之所以采用此方式来初始化mHandler是因为采用了主线程的消息队列,有关Handler,Message等相关介绍会在后续文章中做详细解说)并且重写了handleMessage()方法,在该方法中只处理类型为MSG_EXEC_PENDING_BROADCASTS的消息,当收到此类型的消息时就去调用executePendingBroadcasts()方法,其他情况下转交给其父类来处理,这就是初始化的过程。

        我们接着看注册广播的过程,注册广播也很简单,直接调用LocalBroadcastManager的registerReceiver()方法即可,该方法需要传递BroadcastReceiver和IntentFilter实例,在以上步骤2中我们定义了BroadcastReceiver和IntentFilter,那就看一看LocalBroadcastManager的registerReceiver()方法都做了怎样的操作吧,代码如下:

public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    synchronized (mReceivers) {
    	// 根据传递进来的receiver和filter创建ReceiverRecord实例entry
        ReceiverRecord entry = new ReceiverRecord(filter, receiver);
        // 根据receiver从缓存中查找
        ArrayList<IntentFilter> filters = mReceivers.get(receiver);
        if (filters == null) {
        	// 如果为空就创建,然后加入缓存中
            filters = new ArrayList<IntentFilter>(1);
            mReceivers.put(receiver, filters);
        }
        // 添加操作
        filters.add(filter);
        
        // 循环遍历filter中的action
        for (int i=0; i<filter.countActions(); i++) {
        	// 循环获取每一个action
            String action = filter.getAction(i);
            // 根据action从缓存中查找
            ArrayList<ReceiverRecord> entries = mActions.get(action);
            if (entries == null) {
            	// 如果缓存中不存在就创建,然后加入到缓存中
                entries = new ArrayList<ReceiverRecord>(1);
                mActions.put(action, entries);
            }
            // 添加操作
            entries.add(entry);
        }
    }
}

        代码中的注释说的很详细,那还是大致捋一下流程吧,在该方法中通过synchronize关键字对mReceivers进行了加锁操是为了保证数据的一致性,首先根据传递进来的receiver和filter构建了一个ReceiverRecord对象entry,见名字就可以猜测是一个封装,然后根据receiver从缓存集合mReceivers中查找是否存在对应的filters,如果缓存中不存在filters就新建一个并把该filters存储在mReceivers中,最后把传递进来的filter装载进filters中。缓存完传递进来的receiver和filter后开始循环遍历filter中包含的action,逻辑如刚刚一样,先是根据action从缓存中查找entries,如果不存在就新建一个然后把新建的entries装载进mActions缓存中,最后把最初新建的entry装载进entries中,这就是注册广播接收器的主要流程,核心思想是把传递进来的receiver和filter添加进缓存中。

        我们看一下如何发送一个广播,代码如下:

public boolean sendBroadcast(Intent intent) {
    synchronized (mReceivers) {
    	// 从intent中获取相应值
    	final ContentResolver resolver = mAppContext.getContentResolver();
        final String action = intent.getAction();
        final String type = intent.resolveTypeIfNeeded(resolver);
        final Uri data = intent.getData();
        final String scheme = intent.getScheme();
        final Set<String> categories = intent.getCategories();
        // 根据action从缓存中查找相应值
        ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
        if (entries != null) {
            ArrayList<ReceiverRecord> receivers = null;
            // 缓存中存在对应的action集合entries,开始循环遍历entries
            for (int i=0; i<entries.size(); i++) {
            	// 拿到每一个ReceiverRecord实例receiver
                ReceiverRecord receiver = entries.get(i);
                // 如果receiver.broadcasting为true就跳过本次操作继续循环
                if (receiver.broadcasting) {
                    continue;
                }
                // 调用IntentFilter的match()方法来和intent相匹配,如果返回值小于0表示不匹配
                int match = receiver.filter.match(action, type, scheme, data, categories, "LocalBroadcastManager");
                if (match >= 0) {
                	// 匹配成功
                    if (receivers == null) {
                        receivers = new ArrayList<ReceiverRecord>();
                    }
                    // 把receiver添加进receivers中
                    receivers.add(receiver);
                    // 设置receiver.broadcasting值为true
                    receiver.broadcasting = true;
                }
            }
            // 如果receivers不为空表示匹配到了BroadcastReceiver
            if (receivers != null) {
            	// 循环遍历匹配到的Receiver,把其broadcasting设置为false
                for (int i=0; i<receivers.size(); i++) {
                    receivers.get(i).broadcasting = false;
                }
                // 根据intent和receivers来new一个BroadcastRecord并加入到mPendingBroadcasts集合中
                mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                // 检测消息队列中时候含有Message.what为MSG_EXEC_PENDING_BROADCASTS的消息,如果没有就发送一个消息
                if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                    mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                }
                // 表示发送广播成功
                return true;
            }
        }
    }
    // 发送广播失败
    return false;
}
        sendBroadcast()方法很简单,只接收一个Intent,并且返回一个boolean值(true:广播发送成功,false:广播发送失败)。我们接着往下读代码码,为了保证数据一致性还是给mReceivers进行了加锁操作,先是获取到intent中携带的值,比如:action,type,data,schema,categories等,然后根据intent中的action从mActions缓存中查找entries实例,接下来是个if语句,如果条件成立就进入条件语句否则直接返回false(false可理解为发送广播失败),进入if语句后循环遍历entries集合拿到集合中的每一个ReceiverRecord对象receiver,如果receiver.braodcasting=true则跳过当前操作继续下一轮循环,否则调用receiver.filter.match()方法来和intent的携带值进行匹配,通过查看文档可知match()方法返回int类型的值,如果返回值小于0就表示匹配失败,当match大于等于0时进入if语句内把匹配到的receiver记录下来保存在receivers集合中并且设置其broadcasting值为true,此论循环完成之后receivers中装载的都是符合条件的Receiver了,然后又做了一个循环把刚刚符合条件的Receiver的broadcasting属性全都设置为false,最后把这些值放入到pending集合中,加入集合中之后通过mHandler检查消息对列中是否包含了消息类型为MSG_EXEC_PENDING_BROADCASTS的消息,如果没有包含就发送一个消息类型为MSG_EXEC_PENDING_BROADCASTS的消息,消息发送之后返回true,表示广播发送成功。
        通过mHandler发送消息类型为MSG_EXEC_PENDING_BROADCASTS的消息也就是说需要在mHandler的handleMessage()方法中对此类消息做出响应,在mHandler的handleMessage的方法中调用到了executePendingBroadcasts()方法,我们看看这个方法所做的操作吧,代码如下:
private void executePendingBroadcasts() {
    while (true) {
        BroadcastRecord[] brs = null;
        // 加锁操作
        synchronized (mReceivers) {
        	// 这个加锁的代码块就是把mPendingBroadcasts中存储的值移动到临时变量brs中,然后清空自己
            final int N = mPendingBroadcasts.size();
            if (N <= 0) {
                return;
            }
            brs = new BroadcastRecord[N];
            mPendingBroadcasts.toArray(brs);
            mPendingBroadcasts.clear();
        }
        // 循环遍历brs,最后调用到Receiver的onReceive()方法并把相应值传递进去
        for (int i=0; i<brs.length; i++) {
            BroadcastRecord br = brs[i];
            for (int j=0; j<br.receivers.size(); j++) {
                br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
            }
        }
    }
}
        方法executePendingBroadcats()的逻辑很简单,就是先把缓存中的数据移动到一个临时数组中并把缓存清空,接着依次循环临时数组中存在的BroadcastRecord,然后回调Receiver的onReceive()方法,这就最终导致我们注册的BroadcastReceiver的onReceive()方法得到了调用。注册广播和发送广播的逻辑算是分析完了,注册广播接收器的核心逻辑就是把注册的每一个BroadcastReceiver存放在缓存中;发送广播的核心逻辑就是根据IntentFilter的match()方法做匹配,把匹配到的广播接收器添加到一个新的缓存集合中后发送消息,捕获消息后依次回调BroadcastReceiver的onReceive()方法。既然有注册和发送操作,那肯定也少不了注销操作,这时候你肯定会猜想注销广播接收器的操作应该是把广播接收器从缓存中清除吧?恭喜你答对了,代码如下:
public void unregisterReceiver(BroadcastReceiver receiver) {
    synchronized (mReceivers) {
        ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
        // 缓存中不存在则直接返回
        if (filters == null) {
            return;
        }
        // 循环遍历缓存,找出相匹配的BroadcastReceiver后从缓存中删除
        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);
                    }
                }
            }
        }
    }
}

        注销广播接收器的核心就是从receivers和mActions缓存中删除相关值,到这里有关LocalBroadcastManager的源码算是分析完了,其核心主要是一下两点:

  1. 借助Handler进行消息传递
  2. 使用IntentFilter的match()功能

        前文中提到在应用内发送广播使用LocalBroadcastManager是官方极力推荐的,它不仅安全更且高效,安全主要是用了Handler的消息机制,使消息只能在应用内发送和接收;高效主要是在缓存中直接遍历查询比使用原来的Binder机制进行通信的方式效率要高很多。好了,看到这后你是不是决定不再使用原来方式来发送或接受广播了?呵呵,反正我是抛弃了原来的使用方式……技术分享


        从安全的角度深入理解BroadcastReceiver就告一段落了,感谢收看(*^__^*) …





Android 源码系列之<三>从安全的角度深入理解BroadcastReceiver(下)

标签:

原文地址:http://blog.csdn.net/llew2011/article/details/51152723

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!