LocalBroadcast的使用及简单分析

LocalBroadcast的使用及简单分析

一、LocalBroadcast常用使用方式

BroadcastReceiver(广播接收器)是Android的四大组件之一,可作为事件和数据传递的一种方式,支持一对多。广播主要有全局广播和本地广播之分。相较于全局广播,本地广播只在应用内传递数据,因此更加安全、高效。简单的使用方法如下:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private BroadcastReceiver mBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        setContentView (R.layout.activity_main);

        // 实例化一个广播接收器
        mBroadcastReceiver = new BroadcastReceiver () {
            @Override
            public void onReceive(Context context, Intent intent) {

                // TODO 接收到广播时的逻辑
                Log.d (TAG, "onReceive: " + intent.getStringExtra ("name"));
            }
        };

        // 实例化IntentFilter
        IntentFilter intentFilter = new IntentFilter ();
        intentFilter.addAction ("com.github.xiaogegechen.broadcastreceivertestdemo");
        intentFilter.addAction ("com.github.xiaogegechen.broadcastreceivertestdemo1");

        // 注册本地广播
        LocalBroadcastManager.getInstance (this).registerReceiver (mBroadcastReceiver, intentFilter);

        findViewById (R.id.button).setOnClickListener (new View.OnClickListener () {
            @Override
            public void onClick(View v) {

                // 发送一条广播
                Intent intent = new Intent ();
                intent.setAction ("com.github.xiaogegechen.broadcastreceivertestdemo1");
                intent.putExtra ("name", "Jack");
                LocalBroadcastManager.getInstance (MainActivity.this).sendBroadcast (intent);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy ();

        // 注销该广播接收器
        LocalBroadcastManager.getInstance (this).unregisterReceiver (mBroadcastReceiver);
    }
}

1.首先继承BroadcastReceiver这个抽象类并实现它的onReceive(Context context, Intent intent)方法,这个方法会在该BroadcastReceiver接收到广播时回调。
2.实例化一个IntentFilter,通过设置它的action设置该接收器可以收到的广播类型。
3.然后拿到LocalBroadcastManager的单例并调用它的registerReceiver (BroadcastReceiver receiver, IntentFilter filter)方法,该方法接收两个参数,第一个是广播接收器,第二个参数是一个IntentFilter实例,直接传我们刚刚实例化的两个实例。
4.在任意位置拿到LocalBroadcastManager的单例并调用它的sendBroadcast(Intent intent)方法发送一条广播,在intent中设置action,也可以携带数据。最后当不使用时要及时回收BroadcastReceiver
如果有多个接收器,按照上面的步骤添加即可。由此可见,本地广播使用还是很简单的,而且支持子线程中发广播和多个接收器,但是要注意onReceive(Context context, Intent intent)方法是在UI线程中执行的,我们后面会简单分析。

二、简单分析

本地广播内部基于Handler机制,采用观察者模式,发送广播时通知到每一个注册了该广播的接收器并根据IntentFilter通知相应的接收器。它的主类是LocalBroadcastManager类,而且较短,我们可以简单分析一下。

1、单例

在这里插入图片描述
这里通过懒汉模式实现了一个单例,并拿到了Application级别的Context实例。

2、注册广播接收器

注册时调用的是registerReceiver (BroadcastReceiver receiver, IntentFilter filter)方法。这里面涉及到两个关键的map容器,mReceiversmActions。我们先看一下这两个map:
在这里插入图片描述
mReceivers是以BroadcastReceiver为key,以ArrayList<ReceiverRecord>为value的键值对。我们注册一个receiver可是为其指定多个IntentFilter,而一条广播只要满足一个IntentFilter就可以被这个receiver接收。因此需要存储receiver和它的IntentFilter的映射关系。这个关系就存储在mReceivers中。ReceiverRecord是调用一次registerReceiver (BroadcastReceiver receiver, IntentFilter filter)就会产生的一个广播接收器信息体,它维护了调用一次registerReceiver传来的receiver和它的IntentFilter以及这个接收器的状态,其源码如下:

再看mActions,它以一个字符串为key,以ArrayList<ReceiverRecord>为value。我们知道一个action其实是可以满足多个receiver的,因此需要存储下某个action和它可以满足的receiver的映射关系。这个关系就存储在mActions中。key字符串就是action。value则是该action可以满足的信息体。
然后我们看registerReceiver (BroadcastReceiver receiver, IntentFilter filter)方法就比较好理解了:
在这里插入图片描述
做了两件事:存储receiver和它的IntentFilter的映射关系;存储action和它可以满足的receiver的映射关系。由于注册过程属于原子性操作,所以需要加锁同步。这样就完成了注册环节。

3、注销广播接收器

注销是注册的逆过程,因此也是主要操作那两个map容器了:
在这里插入图片描述
首先也是原子性操作,所以放进synchronized块中。从mReceivers将这个receiver移除,如果返回null,说明没有注册过,mReceiversmActions中就不存在这个receiver的映射关系了,直接跳出该方法。不为空就拿到这个mReceivers中存储的所有以该receiver为key的IntentFilter并遍历,找到每个IntentFilter并遍历它的action,因为这些action已经不再需要满足这个receiver了因此我们要在mActions中找到这种action并以它为key拿到值,在将值中这个receiver移除掉,从而回收资源。而如果移除之后这个action为key的值里面已经没有receiver了,说明这个action不能满足任何接收器,那就需要删除它,完成资源的进一步回收。

4、发送广播

一条广播发出去,是如何保证所有注册的接收器都能按照自己的过滤规则接收到呢?
在这里插入图片描述
在这里插入图片描述
这个方法较长,但是去掉大量log,逻辑很简单。发送广播时传递一个携带actionIntent参数,其实就是拿这个action去遍历匹配mActions中以该action为key的所有receiver条目,如果是已经执行过的receiver就跳过,如果不是的话就加到一个名为receivers的list里面。遍历完毕后在将receivers整体放进一个 名为mPendingBroadcasts的list中。这个mPendingBroadcasts中存储了发送一条广播之后需要通知的所有的receiver。其定义如下:
在这里插入图片描述
然后就是重点了,通过mHandler发了一条消息,而正是这个消息触发了通知的操作看一下这个mHandler在这里插入图片描述
它调了executePendingBroadcasts()方法,跟进去:
在这里插入图片描述
可以看到,在executePendingBroadcasts()中拿到mPendingBroadcasts中存储的需要通知的所有的receiver并放进了数组brs中,然后遍历这个数组并调用其子项的onReceive(Context context, Intent intent)方法,看到这里就明白了,这个onReceive(Context context, Intent intent)方法正是我们实例化receiver时候重写的方法呀!
至此,本地广播运行过程就很清晰了。

三、一些细节问题

我们前面讲onReceive(Context context, Intent intent)方法是在主线程运行,是这样吗?我们看一下
mHandler的实例化过程:
在这里插入图片描述
可以看到mHandlerLooper用的是Application级别的ContextLooper,也就是主线程的Looper,当然executePendingBroadcasts()方法也在主线程,onReceive(Context context, Intent intent)方法也在主线程,所以如果接收到广播后要做耗时操作的话,千万别忘了切换线程。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值