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
容器,mReceivers
和mActions
。我们先看一下这两个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,说明没有注册过,mReceivers
和mActions
中就不存在这个receiver
的映射关系了,直接跳出该方法。不为空就拿到这个mReceivers
中存储的所有以该receiver
为key的IntentFilter
并遍历,找到每个IntentFilter
并遍历它的action
,因为这些action
已经不再需要满足这个receiver
了因此我们要在mActions
中找到这种action
并以它为key拿到值,在将值中这个receiver
移除掉,从而回收资源。而如果移除之后这个action
为key的值里面已经没有receiver了,说明这个action
不能满足任何接收器,那就需要删除它,完成资源的进一步回收。
4、发送广播
一条广播发出去,是如何保证所有注册的接收器都能按照自己的过滤规则接收到呢?
这个方法较长,但是去掉大量log,逻辑很简单。发送广播时传递一个携带action
的Intent
参数,其实就是拿这个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
的实例化过程:
可以看到mHandler
的Looper
用的是Application
级别的Context
的Looper
,也就是主线程的Looper
,当然executePendingBroadcasts()
方法也在主线程,onReceive(Context context, Intent intent)
方法也在主线程,所以如果接收到广播后要做耗时操作的话,千万别忘了切换线程。