LocalBroadcastManager知识点回顾及原理浅析

概述

LocalBroadcastManager作为Android系统广播机制的一种高效方案,可以有效地保护数据的安全性和隐秘性,能够让数据只能在自身应用中传播,不同与系统广播比较复杂的实现机制,因此回顾下其使用方法和原理。

使用

//比如在Activity中使用

//创建IntentFilter,指定触发的action,可以指定多个action
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("test");
intentFilter.addAction("test2");
BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                Log.d("接受",action);
                if (intent.getAction().equals("test")){
                    Log.d("数据",intent.getStringExtra("test"));
                }
            }
        };
//注册本地广播
 LocalBroadcastManager.getInstance(context).registerReceiver(receiver,intentFilter);

然后发送广播:

 Intent intent = new Intent();
 intent.setAction("test");
 intent.putExtra("test","测试数据");
 intent.setData
 LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

控制台输出:

08-01 15:05:23.209 23618-23618/com.franky.testD/接受: test
08-01 15:05:23.209 23618-23618/com.franky.testD/数据: 测试数据

解除注册:

LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);

总结起来就是:
1.创建IntentFilterBroadcastReceiver对象,BroadcastReceiver对象处理回调方法;
2.使用LocalBroadcastManager注册IntentFilterBroadcastReceiver;
3.创建Intent对象,封装对应的action,并可传递数据;
4.使用LocalBroadcastManager发送广播;
3.不需要时候调用LocalBroadcastManager解除注册;

原理

首先看看是如何获取LocalBroadcastManager对象的:

    @NonNull
    public static LocalBroadcastManager getInstance(@NonNull Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
            //使用了ApplicationContext
                mInstance = new LocalBroadcastManager(context.getApplicationContext());
            }
            return mInstance;
        }
    }

就是很典型的一个单例模式,整个应用使用这一个LocalBroadcastManager对象。
接下来看看注册:

 //这个mReceivers对象保存了一个receiver对象可能对应的多个reveiver和IntentFilter的键值对  
 private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
            = new HashMap<>();
//这个mActions 对象保存了action可能对应的多个reveiver和IntentFilter的键值对            
private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();

public void registerReceiver(@NonNull BroadcastReceiver receiver,
            @NonNull IntentFilter filter) {
        synchronized (mReceivers) {
        //ReceiverRecord 就是封装了filter, receiver的一个包装对象
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //从mReceivers中取出对应的filters集合,因为有时候可能使用一个receiver对象
            //和多个IntentFilter注册的情况,所以这里使用了mReceivers来保存这种可能情况
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {//如果为空那么初始化filters 
                filters = new ArrayList<>(1);//默认容量为1,为了优化内存
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);//这里也把封装的entry放进去
            //这里循环取出当前注册的filter的action
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                //从mActions里通过action取出对应的ReceiverRecord集合,因为有时候可能
                //使用一个action和多个不同reveiver对象注册
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {//entries为空的话就初始化一个
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);//放入对应关系
                }
                //放入当前封装的ReceiverRecord对象 
                entries.add(entry);
            }
        }
    }

注册过程主要是两个缓存对象:

  • mReceivers负责保存一个receiver对应多IntentFilter的情况;
  • mActions负责保存一个action对应多receiver的情况;

分析完了注册,看看发送方法的实现:

//mPendingBroadcasts 就是保存了待发送的intent和receiver的集合
private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();

//如果广播被一个或者多个receiver接收到了,那么就返回true
public boolean sendBroadcast(@NonNull Intent intent) {
        synchronized (mReceivers) {
            //获取intent的action及其他信息
            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获取注册时候mAcitons保存的reveiver集合 
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {//只有不为空才能进行发送
                if (debug) Log.v(TAG, "Action list: " + entries);
                //这里准备一个成功匹配的receivers集合
                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);
                    //如果这个reveiver正在发送中,那么跳过
                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }
                    //注册的filter是否去传递的intent的各项信息匹配
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {//大于0就是匹配成功
                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                                Integer.toHexString(match));
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        //这里把匹配成功的reveiver放入集合
                        receivers.add(receiver);
                        //更改发送标识为true
                        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++) {
                    //将发送标识变成false
                        receivers.get(i).broadcasting = false;
                    }
                    //重新包装成BroadcastRecord,加入到mPendingBroadcasts集合中
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    //这里出现了mHandler,说明消息是通过handler发出的,这里通过判断如果正在发送,就直接返回true了
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                    //如果没有发送,那么就发送一个标识,开始进入handler的消息处理机制
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

发送方法主要就是提取出符合当前Intent对象的注册的receiver集合,最后利用Handler消息机制来处理,这个过程也不复杂。
那么看看mHandler的实现:

    //其实在构造LocalBroadcastManager时,mHandler就初始化了
    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        //这里传入了主线程的Looper
        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);
                }
            }
        };
    }

mHandler的处理非常简单,最终会执行到这个executePendingBroadcasts()方法:

private void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                //判断下这个集合的大小,大小为0,就return,结束循环
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                //复制到数组
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                //清空集合,释放内存
                mPendingBroadcasts.clear();
            }
            //双循环取出receiver
            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) {
                    //进行receiver的回调
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }

处理逻辑也很简单,现将mPendingBroadcasts复制到一个临时数组,然后循环取出receiver,如果没有反注册,那么进行回调,发送完毕。
另外LocalBroadcastManager还提供了同步的发送方法:

    public void sendBroadcastSync(@NonNull Intent intent) {
        //这里在mHandler处理之前就返回true了
        if (sendBroadcast(intent)) {
        //由于executePendingBroadcasts方法是在当前线程,所以会阻塞当前线程
            executePendingBroadcasts();
        }
    }

最后来看看反注册的实现:

public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
        synchronized (mReceivers) {
        //使用remove方法取出reveiver对应的filters集合
            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
            if (filters == null) {//没有就直接reture
                return;
            }
            //开始循环删除
            for (int i=filters.size()-1; i>=0; i--) {
                final ReceiverRecord filter = filters.get(i);
                filter.dead = true;//将标识设为true
                //循环该filter的action
                for (int j=0; j<filter.filter.countActions(); j++) {
                    final String action = filter.filter.getAction(j);
                    //根据action取出对应的receivers
                    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);
                            //判断缓存的reveiver和传入的receive是否是一个对象
                            //如果是一个对象,改变标识为true,并从集合中移除
                            if (rec.receiver == receiver) {
                                rec.dead = true;
                                receivers.remove(k);
                            }
                        }
                        //如果receivers没有对象了,说明这个action没有receiver接受了
                        //那么从mActions集合中移除这个action
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

反注册的流程总结下:

  • 首先移除receiver对应的filters集合;
  • 从这个filters集合中循环所有的action;
  • mActions集合中取出对应actionreceivers集合;
  • receivers集合中移除和反注册receiver对象相同的ReceiverRecord 对象
  • 如果对应的receivers没有任何receiver了,那么从mActions集合中移除整个action

总结

  • LocalBroadcastManager只能动态注册广播;
  • 在不需要的时候尽量早的反注册receiver;
  • LocalBroadcastManager只能在单进程应用内使用,无法进行跨进程通信;

猜你喜欢

转载自blog.csdn.net/franky814/article/details/81358708