Android组件间通信之LocalBroadcastManager

Android组件间通信之LocalBroadcastManager

Android开发中,大家都使用过全局广播(global broadcast)。
优点是操作简单,还可以进行跨进程通信。
缺点是全局广播涉及跨进程效率不高,而且因为全局广播发送其他应用也可以注册相应的监听,数据安全性得不到保证。

所以正因为使用的便携性所以少部分开发者会滥用广播,导致程序运行效率低及不可靠的安全性。

LocalBroadcastManager介绍

Android在V4包中给开发者提供了一个类LocalBroadcastManager,中文翻译是本地广播。他相较于全局广播有以下几点优势:
1. 你发送的广播只会在本应用(本进程)传播,不用担心会泄露隐私数据
2. 其他应用也不无法发送广播该类型广播至你的应用,所以不用担心安全性
3. 相比全局广播更加高效
4. 使用便捷,Api与全局广播一致

通过这些优点,我们可以发现LocalBroadcastManager很适合做应用内通信。

LocalBroadcastManager源码分析

我们一起来看看LocalBroadcastManager的源码

LocalBroadcastManager源码不多而且很简单,几个重要的方法:

LocalBroadcastManager#registerReceiver
LocalBroadcastManager#unregisterReceiver
LocalBroadcastManager#sendBroadcast

我们会发现LocalBroadcastManager其实是个单例,从这里就可以知道它只能在单一进程中使用。

public static LocalBroadcastManager getInstance(Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                mInstance = new LocalBroadcastManager(context.getApplicationContext());
            }
            return mInstance;
        }
    }

LocalBroadcastManager#registerReceiver
代码不多,主要是做了以下件事:
1. 将传入的receiver与filter保存至内部类ReceiverRecord
2. 建立receiver与filter映射
3. 建立Action与ReceiverRecord间的映射,便于通过Action直接找到receiver

public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            // ReceiverRecord 内部类,保存了receiver与filter
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<IntentFilter>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(filter);
            // 建立Action与ReceiverRecord间的映射,便于通过Action直接找到receiver
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

LocalBroadcastManager#unregisterReceiver
1. 反注册receiver后移除mReceivers中保存的receiver引用
2. 移除mActions中保存的对应action

public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            for (int i=0; i<filters.size(); i++) {
                IntentFilter filter = filters.get(i);
                // filter对应多个action
                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--;
                            }
                        }
                        // action对应的receiver为空时,移除mActions
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

其实看到这里,就很明白了。这里的注册与反注册只是将receiver对象引用添加到LocalBroadcastManager的缓存队列中进行管理。是很典型的一种观察者模式。

接下来的套路应该就是收到改变通知订阅的观察者了,这也就是LocalBroadcastManager#sendBroadcast的作用:
1. 发送方通过sendBroadcast通知订阅的观察者,传的Intent与普通广播一致
2. 解析intent参数,在队列中找到匹配的receiver 添加到待执行list
3. 通过handler发送消息方式异步执行回调
4. 遍历通知对应业务方

    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            // 解析intent参数,用于后面的匹配
            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);
            // 通过action获取到对应的receiver list
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                ArrayList<ReceiverRecord> receivers = null;
                // 遍历receivers
                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;
                    }

                    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>();
                        }
                        // 将匹配成功的receiver改变标志位并添加到List中
                        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;
                    }
                    // 将符合条件的receiver添加到待执行列表
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                    // 通过handler发送消息的方式将回调异步执行
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }
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);
                }
            }
        };
    }

private void executePendingBroadcasts() {
    while (true) {
        BroadcastRecord[] brs = null;
        synchronized (mReceivers) {
        final int N = mPendingBroadcasts.size();
        if (N <= 0) {
            return;
        }
        brs = new BroadcastRecord[N];
        mPendingBroadcasts.toArray(brs);
        mPendingBroadcasts.clear();
        }
        for (int i=0; i<brs.length; i++) {
        BroadcastRecord br = brs[i];
        // 遍历receiver,进行回调
        for (int j=0; j<br.receivers.size(); j++) {
            br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
        }
        }
    }
}

LocalBroadcastManager重要的代码就这么多,即简单又很精妙。是学习观察者模式的一种好方式。

总结

这样LocalBroadcastManager源码我们就分析完了,看到这里很多人就会想:“这不就是个普通的观察者模式,每天都在写,有必要用这个么?”

其实我们很多人在开发中都会遇到这种情况,一些状态的变化需要及时通知其他业务方,一般就需要写一个观察者管理类,通知订阅的观察者。这样的管理类都要写一套维护观察者的add,remove,notify,工作量较大,而且业务类需要实现的回调也会变的太多。

我们可以直接使用系统提供的LocalBroadcastManager,统一管理,把维护观察者的职责交给它,提升开发工作效率,而且因为Api的一致性可以结合全局广播做到跨进程通信,何乐而不为呢?

当然这种模式有个缺陷就是不能传递非序列化对象,解析Intent较为繁琐。

    原文作者:Monster-L
    原文地址: https://blog.csdn.net/u013085697/article/details/79282536
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞