美文网首页
LocalBroadcastManager源码学习

LocalBroadcastManager源码学习

作者: 大棋17 | 来源:发表于2019-05-15 16:47 被阅读0次

            BroadcastReceiver作为android四大组件之一常常被人使用。但无论是普通广播还是有序广播,都是系统全局广播。即发送的广播可以被任何应用程序接收到,但同时我们也能接收来自任何应用程序的广播。

            为了解决广播安全性的问题,Google引入了一套本地广播机制。使用本地广播机制发送的广播只能在应用中进行传递,而广播接收器也只能接收来自应用内的广播。

    LocalBroadcast简单使用

            本地广播的使用和普通广播基本没区别,主要是使用LocalBroadcastManager进行广播发送、注册广播接收器和注销广播接收器。

    #daqiActivity.java
    //本地广播管理器
    private LocalBroadcastManager mBroadcastManager;
    //广播接收器
    private daqiBroadcastReceiver mBroadcastReceiver;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //.....
        
        //获取广播管理类实例
        mBroadcastManager = LocalBroadcastManager.getInstance(daqiActivity.this);
        //初始化intent拦截器
        IntentFilter mIntentFilter = new IntentFilter();
        mIntentFilter.addAction("com.daqi.demo.LOCAL_BROADCAST");
        //初始化广播接收器
        mBroadcastReceiver = new daqiBroadcastReceiver();
        //注册广播接收器
        mBroadcastManager.registerReceiver(mBroadcastReceiver,mIntentFilter);
    
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.daqi.demo.LOCAL_BROADCAST");
                //发送本地广播
                mBroadcastManager.sendBroadcast(intent);
            }
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注销广播接收器
        mBroadcastManager.unregisterReceiver(mBroadcastReceiver);
    }
    
    private class daqiBroadcastReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(daqiActivity.this,"本地广播",Toast.LENGTH_SHORT).show();
        }
    }
    

    LocalBroadcastManager源码分析

            从LocalBroadcastManager.getInstance(this)获取实例可以看出,LocalBroadcastManager是一个单例管理类。

    查看LocalBroadcastManager#getInstance()的源码并无异样,继续查看构造方法:

    #LocalBroadcastManager.java
    @NonNull
    public static LocalBroadcastManager getInstance(@NonNull Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                //获取ApplicationContext的context
                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);
                }
            }
        };
    }
    

    LocalBroadcastManager的构造函数只做了两件事:

            1、存储ApplicationContext的Context。

            2、初始化一个Handler,并将其绑定在主线程。

    LocalBroadcastManager居然初始化了一个Handler,莫非本地广播也是通过Handler实现的?

    LocalBroadcastManager内部类

            在查看注册广播监听器、注销广播监听器和发送本地广播的源码之前,需要先了解LocalBroadcastManager得两个内部类ReceiverRecord 和 BroadcastRecord。

    #LocalBroadcastManager.java
    //接收器记录
    private static final class ReceiverRecord {
        final IntentFilter filter;
        final BroadcastReceiver receiver;
        //标记是否正在处于广播状态
        boolean broadcasting;
        //标记该接收器记录是否有效
        //false为有效,true为无效。
        boolean dead;
    
        ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
            filter = _filter;
            receiver = _receiver;
        }
    
        //....
    }
    
    //广播器记录
    private static final class BroadcastRecord {
        //发送广播时的Intent对象
        final Intent intent;
        //接收器记录列表
        final ArrayList<ReceiverRecord> receivers;
    
        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
            intent = _intent;
            receivers = _receivers;
        }
    }
    

            从名字上ReceiverRecord-接收器记录,BroadcastRecord-广播器记录,可以很好的理解各类中变量的意义。

            ReceiverRecord主要用于存储注册广播接收器时,存储注册广播时传递的参数:IntentFilter 和 BroadcastReceiver。

            BroadcastRecord主要存储广播的intent和存储IntentFilter符合该intent.action的ReceiverRecord对象。

    LocalBroadcastManager内部集合

    #LocalBroadcastManager.java
    
    //存储广播接收器和对应接收器记录列表(ArrayList<LocalBroadcastManager.ReceiverRecord>)
    //在之前的版本这里value存储的是ArrayList<IntentFilter>
    //LocalBroadcastManager.ReceiverRecord中存有IntentFilter,所以存储ArrayList<LocalBroadcastManager.ReceiverRecord>和ArrayList<IntentFilter>道理都一样
    private final HashMap<BroadcastReceiver, ArrayList<LocalBroadcastManager.ReceiverRecord>> mReceivers = new HashMap();
    
    //String存储action,表示一个action对应多个接收该action的接收器记录(ReceiverRecord)
    //(ReceiverRecord内部存储广播接收器和过滤规则)
    private final HashMap<String, ArrayList<LocalBroadcastManager.ReceiverRecord>> mActions = new HashMap();
    
    //与发送的广播的Action匹配的ReceiverRecord集合
    //即存储了广播接收器、广播过滤规则 和 发送广播时的Intent
    private final ArrayList<LocalBroadcastManager.BroadcastRecord> mPendingBroadcasts = new ArrayList();
    

    LocalBroadcastManager定义了3个集合,简单的说就是:

    • mReceivers    存储广播接收器(BroadcastReceiver)对象 和 其IntentFilter信息。
    • mActions    存储单个action 和 监听该action的接收器记录(ReceiverRecord)列表
    • mPendingBroadcasts    存储发送广播时携带的intent对象 和 符合该intent.action的IntentFilter。

    如果看不懂,暂时放下,配合下面的源码分析食用。

    分别从广播机制的注册广播监听器、发送广播、注销广播监听器入手查看源码。
    先从注册广播监听器入手:

    registerReceiver()

    #LocalBroadcastManager.java
    
    public void registerReceiver(@NonNull BroadcastReceiver receiver,
            @NonNull IntentFilter filter) {
        //加锁
        synchronized (mReceivers) {
            //传递进来的receiver广播接收器与filter广播拦截器会被封装在ReceiverRecord对象中。
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //从mReceivers中获取有关该receiver广播接收器的接收器记录(ReceiverRecord)对象
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            //如果没有获取到对应的记录(正常是null)
            if (filters == null) {
                //创建一个size为1的ArrayList,并将信息存储进去。
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            //将存储注册广播信息的内容存储到filters中
            filters.add(entry);
            //遍历注册时传递进来的IntentFilter的action列表
            for (int i=0; i<filter.countActions(); i++) {
                //获取action
                String action = filter.getAction(i);
                //获取特定action对应的接收器记录
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                //如果原本没有这个条action的记录
                if (entries == null) {
                    //则创建一个存进去
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                //将这次注册广播监听器生成的接收器记录对象存储到该action对应的接收器记录(ReceiverRecord)器列表中。
                entries.add(entry);
            }
        }
    }
    

    总的来说,LocalBroadcastManager#registerReceiver()做了三件事:

    • 将注册广播监听器的信息:IntentFilter 和 BroadcastReceiver。封装在ReceiverRecord中。
    • 为广播接收器添加IntentFilter,存储在mReceivers中。
    • 为注册时IntentFilter中的action添加接收器记录(ReceiverRecord),存储在mActions中。

    unregisterReceiver()

    再查看注销广播接收器的源码:

    #LocalBroadcastManager.java
    
    public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
        //加锁
        synchronized (mReceivers) {
            //移除该广播接收器的IntentFilter规则
            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            //遍历其存储的所有IntentFilter
            for (int i=filters.size()-1; i>=0; i--) {
                //取出其中一个ReceiverRecord对象,其实是获取其对应的IntentFilter
                final ReceiverRecord filter = filters.get(i);
                //将该接收器记录标志为失效(死亡)
                filter.dead = true;
                //遍历这个IntentFilter过滤规则的action
                for (int j=0; j<filter.filter.countActions(); j++) {
                    //获取其中一个action
                    final String action = filter.filter.getAction(j);
                    //依据该action获取对应的ReceiverRecord列表,即获取其对应的广播接收器(BroadcastReceiver)列表
                    final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    //如果该action的接收器记录(ReceiverRecord)列表不为空
                    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;
                                receivers.remove(k);
                            }
                        }
                        //如果接收器记录(ReceiverRecord)列表移除后为空,即该action无对应的广播接收器,则将该action移除。
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }
    

    注销广播接收器和注册广播接收器做了相反的事情:

    • 在mReceivers中,移除广播接收器和其IntentFilter。
    • 在mActions中,移除IntentFilter#action对应的接收器记录(ReceiverRecord),若action无对应的接收器记录(ReceiverRecord),则将action移除。

    sendBroadcast()

    #LocalBroadcastManager.java
    
    public boolean sendBroadcast(@NonNull 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();
                
            //根据intent对象的action获取其对应的接收器记录(ReceiverRecord)列表
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            //如果该action的接收器记录(ReceiverRecord)列表不为空
            if (entries != null) {
               //..
               
                //这个列表用来记录实际用来接收该广播的广播接收器(BroadcastReceiver)
                ArrayList<ReceiverRecord> receivers = null;
                for (int i=0; i<entries.size(); i++) {
                    //获取单个接收器记录,即获取单个广播接收器
                    ReceiverRecord receiver = entries.get(i);
                    //..
                    
                    //如果当前广播接收器正在广播,则先跳过,防止重复注册
                    if (receiver.broadcasting) {
                        //..
                        continue;
                    }
                    
                    //测试此过滤器是否与给定的意图数据匹配。 只有当Intent中的操作和类别与过滤器匹配时,匹配才会成功
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    //匹配成功
                    if (match >= 0) {
                        //..
                        //如果存储实际用来接收该广播的广播接收器列表没初始化,则初始化。
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        //将该广播接收器存储到列表中,并设置为正在广播的状态。
                        receivers.add(receiver);
                        receiver.broadcasting = true;
                    } else {
                       //..
                    }
                }
                
                //最后接受广播的广播接收器列表不为空,则将intent对象传递进去。
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        //恢复不是正在广播的状态
                        receivers.get(i).broadcasting = false;
                    }
                    //将该广播的intent对象和广播接收器列表封装成接收器记录,存储在mPendingBroadcasts中。
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                       //发送特定what的Message,交由Handler处理。 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }
    

    sendBroadcast()做了4个步骤:

            1、获取广播的intent信息,用于后续的匹对。

            2、通过广播的action在mActions中获取其对应的接收器记录(ReceiverRecord)列表。

            3、遍历接收器记录(ReceiverRecord)列表,将列表内元素的IntentFilter对象与广播的intent对象进行匹对,如果匹对成功,则存储在新的接收器记录(ReceiverRecord)列表receivers中。

            4、将新的接收器记录(ReceiverRecord)列表receivers 和 广播的intent对象存储在广播记录(BroadcastRecord)对象中,并将该广播记录(BroadcastRecord)对象存储在mPendingBroadcasts中。

            5、发送what = MSG_EXEC_PENDING_BROADCASTS的Message,让Handler传递广播intent对象到对应的接收器中。

    注:对列表receivers存储接收器记录过程中,将接收器记录中的broadcasting标记为true,可以防止重复注册广播接收器造成的多次广播。

    重归Handler的handleMessage方法中:

    #LocalBroadcastManager.java
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_EXEC_PENDING_BROADCASTS:
                executePendingBroadcasts();
                break;
            default:
                super.handleMessage(msg);
        }
    }
    

    what = MSG_EXEC_PENDING_BROADCASTS的Message,会调用executePendingBroadcasts进行处理。

    #LocalBroadcastManager.java
    void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                //获取待广播的广播记录对象。
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }mPendingBroadcasts
                //将广播记录BroadcastRecord对象存储在数组中,并清空mPendingBroadcasts对象。
                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);
                    //广播接收器列表没有被标记失效,则进行onReceive()回调进行广播。
                    if (!rec.dead) {
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }
    

            executePendingBroadcasts()将mPendingBroadcasts转换为数组,遍历广播记录数组。对还生效的接收器对象进行onReceive()回调,进行广播。

    注:当广播接收器移除时,ReceiverRecord#dead会被标记为true,则标记为无效。

    再回看3个列表对象:

    • mReceivers    存储广播接收器(BroadcastReceiver)对象 和 其IntentFilter信息。</br>
      作用:注销广播接收器时,通过广播接收器对象,寻找到其IntentFilter列表信息,方便根据IntentFilter列表中的action信息在mActions中进行移除。
    • mActions    存储单个action 和 监听该action的接收器记录(ReceiverRecord)列表。</br>
      作用:发送广播时,根据广播中intent对象的action,可以快速寻找到其对应接收的接收记录对象,再根据接收器对象的IntentFilter与广播中intent对象做匹配,寻找符合该广播intent对象条件的广播接收器对象。
    • mPendingBroadcasts    存储发送广播时携带的intent对象 和 符合该intent.action的IntentFilter。</br>
      作用:短暂存储需要传递的广播intent对象和接收的该intent对象广播接收器。

    总结:

            本地广播其实也是使用handler机制,将广播接收器与action、IntentFilter相关联。因为Handler的原因,本地广播也就只能在该应用内接收。

    相关文章

      网友评论

          本文标题:LocalBroadcastManager源码学习

          本文链接:https://www.haomeiwen.com/subject/xpcmaqtx.html