Android LocalBroadcastManager 原理

概述

是什么

一个用于在「当前进程空间」内「注册」 Receiver 和「发送」 Intent 广播的辅助类。

解决什么问题

某个进程空间内多个模块间通信问题

使用方法

基本使用方法

        // 拿到 LocalBroadcastManager 单例
        final LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // 接收到广播
                Log.d(TAG, "obj@" + System.identityHashCode(this) + " " +
                        "onReceive() called with: context = [" + context + "], " +
                        "intent = [" + intent + "]");
                // 反注册 Receiver
                manager.unregisterReceiver(this);
            }
        };
        IntentFilter filter = createFilter(ACTION_0);
        // 注册 Receiver
        manager.registerReceiver(broadcastReceiver, filter);
        // 发送广播
        manager.sendBroadcast(createIntent(ACTION_0));

进阶使用方法

在发送线程同步回调接收者 sendBroadcastSync

        final LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);

        final BroadcastReceiver receiver0 = createBroadcastReceiver();

        // 注册观察者
        manager.registerReceiver(receiver0, createFilter(ACTION_0));

        new Thread(new Runnable() {
            @Override
            public void run() {
                // 在后台线程发送同步广播
                manager.sendBroadcastSync(createIntent(ACTION_0));
            }
        }).start();

输出 log 信息解读:
在「01-14 06:09:39.553」时间点,包名为 com.example.guangli.broadcastmanager 的应用进程「3512」 空间内后台线程 「3527」 中,地址为 「151810842」 的 Receiver 对象收到 onReceive 消息

01-14 06:09:39.553 3512-3527/com.example.guangli.broadcastmanager D/MainActivity: 
obj@151810842 onReceive() called with: context = [android.app.Application@57b1e4b],
 intent = [Intent { act=com.liguang.action0 }]

内部原理

抓住本质

  • 观察者模式
  • 订阅/发布模式
  • 生产者/消费者模式
  • 数组和映射表

注册流程

最终目的是记录调用者输入的 Receiver 对象

    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            // 封装 receiver 感兴趣的 filter 到 ReceiverRecord 对象内部
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
               // 添加到 mReceivers 记录下来
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            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);
                    // 建立 action 到 receiver 的映射,降低发送广播时查找复杂度
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

发送广播流程

生产者往容器中写入数据,最终目的找到对 Intent 感兴趣的 Receiver 对象,并通过 Handler 切换到主线程派发 Intent

    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            ...
            // 找到对 action 感兴趣的 receiver
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                ArrayList<ReceiverRecord> receivers = null;
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    // 让 Filter 计算 match 数值
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        receivers.add(receiver);
                    }
                }

                if (receivers != null) {
                    // 添加到待广播队列
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        // 切换到主线程消费待广播队列
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

消费者从容器中读取数据,并清空容器

    private void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            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++) {
                final BroadcastRecord br = brs[i];
                final int nbr = br.receivers.size();
                for (int j=0; j<nbr; j++) {
                    final ReceiverRecord rec = br.receivers.get(j);
                    if (!rec.dead) {
                        // 把 Intent 派发给 Receiver
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }

反注册流程

目的是从 LocalBroadcastManager 内部移除对 receiver 的所有引用

    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            // 从 mReceivers 移除
            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            for (int i=filters.size()-1; i>=0; i--) {
                final ReceiverRecord filter = filters.get(i);
                filter.dead = true;
                for (int j=0; j<filter.filter.countActions(); j++) {
                    final String action = filter.filter.getAction(j);
                    final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=receivers.size()-1; k>=0; k--) {
                            final ReceiverRecord rec = receivers.get(k);
                            if (rec.receiver == receiver) {
                                rec.dead = true;
                                // 从 action => receivers 映射移除
                                receivers.remove(k);
                            }
                        }
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

优缺点

优点

  • 从输出角度来看,广播数据不会离开当前进程空间,所以不必担心泄漏隐私数据
  • 从输入角度来看,其他进程空间也不能向我们发送广播,所以不必担心其他进程可以利用我们的安全漏洞
  • 高效性,数据不必在整个系统空间中经过两次 Binder 进程间通信

缺点

  • 不支持 Sticky 特性,某些需要后注册 Receiver 也可以收到信息的特殊需求无法实现
  • 不支持 order 特性,Intent 分发过程中无法支持某个 Receiver 已经吃掉该 Intent 的某些特性
  • 多线程并发性能不高,内部使用同一把锁对容器的读写进行保护,所有读写需要串行,没有 CopyOnWrite 特性

练习题

  • 删除所有接口内部实现,从零开始实现接口,锻炼写接口能力
  • 不用内部定义的新的类,自己设计数据结构,锻炼设计数据结构能力

总结

  • 使用方便,对外的接口只有 5 个
  • 数据结构简单,内部只有 ReceiverRecord 和 BroadcastRecord 新的类型,使用 HashMap 和 ArrayList 记录信息
  • 算法简单,基本是对 HashMap 和 ArrayList 的读写,只理解主要信息处理流程,没有过多其他逻辑,总共 300+ 行
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。