LocalBroadcastManager源码学习

        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 receivers;

    BroadcastRecord(Intent _intent, ArrayList _receivers) {
        intent = _intent;
        receivers = _receivers;
    }
}

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

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

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

LocalBroadcastManager内部集合

#LocalBroadcastManager.java

//存储广播接收器和对应接收器记录列表(ArrayList)
//在之前的版本这里value存储的是ArrayList
//LocalBroadcastManager.ReceiverRecord中存有IntentFilter,所以存储ArrayList和ArrayList道理都一样
private final HashMap> mReceivers = new HashMap();

//String存储action,表示一个action对应多个接收该action的接收器记录(ReceiverRecord)
//(ReceiverRecord内部存储广播接收器和过滤规则)
private final HashMap> mActions = new HashMap();

//与发送的广播的Action匹配的ReceiverRecord集合
//即存储了广播接收器、广播过滤规则 和 发送广播时的Intent
private final ArrayList 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 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 entries = mActions.get(action);
            //如果原本没有这个条action的记录
            if (entries == null) {
                //则创建一个存进去
                entries = new ArrayList(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 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 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 categories = intent.getCategories();
            
        //根据intent对象的action获取其对应的接收器记录(ReceiverRecord)列表
        ArrayList entries = mActions.get(intent.getAction());
        //如果该action的接收器记录(ReceiverRecord)列表不为空
        if (entries != null) {
           //..
           
            //这个列表用来记录实际用来接收该广播的广播接收器(BroadcastReceiver)
            ArrayList receivers = null;
            for (int i=0; i= 0) {
                    //..
                    //如果存储实际用来接收该广播的广播接收器列表没初始化,则初始化。
                    if (receivers == null) {
                        receivers = new ArrayList();
                    }
                    //将该广播接收器存储到列表中,并设置为正在广播的状态。
                    receivers.add(receiver);
                    receiver.broadcasting = true;
                } else {
                   //..
                }
            }
            
            //最后接受广播的广播接收器列表不为空,则将intent对象传递进去。
            if (receivers != null) {
                for (int i=0; i

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

        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的原因,本地广播也就只能在该应用内接收。

你可能感兴趣的:(LocalBroadcastManager源码学习)