LocalBroadcastManager源码剖析

Handler系列文章:

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

总结:

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

转载于:https://juejin.im/post/5cd4ee27f265da035f6ff2f2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值