我们都知道Android的四大组件,分别是:ActivityServiceContentProvider以及BroadcastReceiver,实际开发中前两者接触的更多一点,后面两个虽然不怎么常用但是偶尔也会接触到,今天我们要说的就和BroadcastReceiver有关,当我们想要去使用BroadcastReceiver会看到官方的提示:如果你不需要应用间的通信,可以考虑使用LocalBroadcastManager,会有更高的执行效率,因为它不涉及进程间通讯,而且不用担心普通广播可能产生的一些安全性问题,
LocalBroadcastManager是何许人也,听着好像是普通广播的阉割版,实际使用上看,他们确实有些相似,只是LocalBroadcast不能实现跨进程,但当我们揭开它神秘面纱,你就会发现,它其实和普通的广播一点关系都没有,如果非得扯出点关系的话,那就是他们都借助了BroadcastReceiver这个类来担当receiver的角色,

基本使用

如果你之前有使用过普通的广播,你会发现在方法调用上,LocalBroadcastManager和普通的广播是一模一样,不同的LocalBroadCastManager的调用方不再是context,而是LocalBroadCastManager的实例,所以所有的逻辑都是在LocalBroadCastManager的掌控之内。首先,我们还是从最基本的使用场景出发,从最基本的使用方法开始跟踪,看它是怎么将消息传递的:

               1                      2                      3                      4                      5                      6                      7                      8                      9                      10                      11                      12                      13                      14                      15                      16                      17                      18                      19                      20                      21                      22                      23                      24                      25                      26                      27                      28                      29                      30                      31                      32                      33                      34                      35        
               public class Test extends Activity {                      private static final String ACTION = "simple_action";                      private static final String DATA = "data";                             BroadcastReceiver mReceiver;                             @Override                      protected void onCreate(Bundle savedInstanceState) {                      super.onCreate(savedInstanceState);                      // 新建一个receiver                      mReceiver = new MyReceiver();                      // 注册receiver                      LocalBroadcastManager.getInstance(this)                      .registerReceiver(mReceiver, new IntentFilter(ACTION));                             // 发送消息                      Intent messageIntent = new Intent(ACTION);                      messageIntent.putExtra(DATA, "给xxx的一封信");                      LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);                      }                             @Override                      protected void onDestroy() {                      // 取消注册                      LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);                      }                             class MyReceiver extends BroadcastReceiver {                      @Override                      public void onReceive(Context context, Intent intent) {                      // 处理消息                      Log.i("TAG", "收到一封信:" + intent.getStringExtra(DATA));                      }                      }                      }        

上面这段代码,就是最常见的LocalBroadcastManager的使用方式了,有没有似曾相识的感觉,没错,在EventBus中用到的也是类似功能的方法,发现都是一样的套路,无非就是注册,取消注册,发送消息以及处理消息,知道了具体的套路,那
么我们就来看一下它是具体怎么实现的。

熟悉的套路

从注册开始,不过在注册之前,我们要拿到一个LocalBroadcastManager的实例,所以先从LocalBroadcastManager.getInstance()开始:

初始化

               1                      2                      3                      4                      5                      6                      7                      8                      9                      10                      11                      12                      13                      14                      15                      16                      17                      18                      19                      20                      21                      22                      23                      24                      25                      26                      27        
               public static LocalBroadcastManager getInstance(Context context) {                      synchronized (mLock) {                      if (mInstance == null) {                      // 在这里,我们看到不管我们传过来的是哪种类型的context,最后构造方法用的都只是applicationContext,所以不用担心内存泄露的问题                      mInstance = new LocalBroadcastManager(context.getApplicationContext());                      }                      return mInstance;                      }                      }                             private LocalBroadcastManager(Context context) {                      mAppContext = context;                      // 看到了熟悉的Handler,而且用的还是mainLooper,已经猜到一些眉目                      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采用的是经典的单例实现,所以它并没有用我们传入的context,因为那样肯定会导致内存泄露,而是保留的applicationContext
LocalBroadcastManager的构造方法中,我们还看到了利用context.getMainLooper()创建了一个主线程的Handler,所以我们可以猜到,它的线程间通信很有可能是通过Handler实现的,后面的分析会慢慢证实我们的猜想,继续往下看注册:

注册

               1                      2                      3                      4                      5                      6                      7                      8                      9                      10                      11                      12                      13                      14                      15                      16                      17                      18                      19                      20                      21                      22                      23                      24                      25                      26                      27                      28                      29                      30                      31        
               // 和EventBus如出一辙的套路,同样定义了两个HashMap                      // 一个以receiver为键,保存receiver相关的所有匹配规则,方便取消注册的时候处理                      // 一个以action为键,保存和这个action相关的所有ReceiverRecord,类比EventBus中的Subscription                      private final HashMap> mReceivers = new HashMap<>();                                    private final HashMap> mActions = new HashMap<>();                                    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {                      synchronized (mReceivers) {                      // 新建一条订阅记录                      ReceiverRecord entry = new ReceiverRecord(filter, receiver);                      // 新增一条和这个receiver的匹配记录                      ArrayList filters = mReceivers.get(receiver);                      if (filters == null) {                      filters = new ArrayList(1);                      mReceivers.put(receiver, filters);                      }                      filters.add(filter);                                    // 将filter中的所有action取出,给相应的action增加订阅记录                      for (int i=0; i entries = mActions.get(action);                      if (entries == null) {                      entries = new ArrayList(1);                      mActions.put(action, entries);                      }                      entries.add(entry);                      }                      }                      }        

和EventBus一样的套路,保存了两个HashMap,一个用于缓存和这个receiver相关的所有匹配规则,一个用于缓存action相关的所有订阅记录,前者主要用在unRegister的时候,方便将和这个receiver相关的所有匹配规则一次性移除;而后者就主要用在匹配事件上,当收到新的广播,根据广播中的action取出所有和这个action有关的所有订阅记录,然后挨个通知订阅记录中的receiver处理事件。在这点处理上,EventBusLocalBroadcast一模一样,趁热打铁,我们决定先看一下取消注册的逻辑来验证我们的猜想:

取消注册

               1                      2                      3                      4                      5                      6                      7                      8                      9                      10                      11                      12                      13                      14                      15                      16                      17                      18                      19                      20                      21                      22                      23                      24                      25                      26                      27                      28        
               public void unregisterReceiver(BroadcastReceiver receiver) {                      synchronized (mReceivers) {                      // 首先移除和这个receiver相关的所有匹配规则                      ArrayList filters = mReceivers.remove(receiver);                      if (filters == null) {                      return;                      }                      // 然后把匹配规则中所有的action取出,在每个action的订阅列表中,把和这个receiver相关的订阅记录移除                      for (int i=0; i receivers = mActions.get(action);                      if (receivers != null) {                      for (int k=0; k     

和我们猜测的一模一样,在注册的时候两个HashMap都在这里发挥了作用,将receiver的引用移除,并且把与之相关所有的订阅事件都移除

发送事件

               1                      2                      3                      4                      5                      6                      7                      8                      9                      10                      11                      12                      13                      14                      15                      16                      17                      18                      19                      20                      21                      22                      23                      24                      25                      26                      27                      28                      29                      30                      31                      32                      33                      34                      35                      36                      37                      38                      39                      40                      41                      42                      43                      44                      45                      46                      47                      48                      49                      50        
               public boolean sendBroadcast(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();                                    // 先匹配action,将相应action下的订阅记录列表取出                      ArrayList entries = mActions.get(intent.getAction());                      if (entries != null) {                             ArrayList receivers = null;                      for (int i=0; i= 0) {                      if (receivers == null) {                      receivers = new ArrayList();                      }                      receivers.add(receiver);                      receiver.broadcasting = true;                      }                      }                             if (receivers != null) {                      for (int i=0; i     

又是熟悉的一幕:两步匹配,先匹配action,找到所有符合action的订阅记录,然后再通过IntentFilter.match()方法精准匹配,确定找到的每条订阅记录都是符合要求的。然后把原始的intent和它的receivers组装成一条BroadcastRecord,并添加到待处理列表,然后检查Handler是否已经在处理事件,如果没有则发一条特定消息唤醒它,然后进入消息处理阶段。
由此,我们可以看到,不同于EventBus发送事件和处理事件有时候是同步的,LocalBroadcast都是异步的,sendBroadcast只是将根据要发送的intent找到它合适的receivers列表,然后添加到待处理列表,然后通过发送Handler消息的方式唤醒广播处理器,所以发送广播不用担心堵塞线程

处理事件

发送广播的最后,我们看到它发送了一条用于唤醒广播处理过程的消息,

               1                      2                      3                      4                      5                      6                      7                      8                      9                      10                      11                      12                      13                      14                      15                      16                      17                      18                      19                      20                      21                      22                      23                      24                      25        
               private void executePendingBroadcasts() {                      // 这是一个无限循环,只有把待处理广播列表都处理完才会退出                      while (true) {                      BroadcastRecord[] brs = null;                      // 为了减少对mReceivers的加锁时间,把列表的元素转移到另一个数组中                      synchronized (mReceivers) {                      final int N = mPendingBroadcasts.size();                      if (N <= 0) {                      return;                      }                      brs = new BroadcastRecord[N];                      mPendingBroadcasts.toArray(brs);                      mPendingBroadcasts.clear();                      }                      // 将待处理广播都取出来进行处理                      for (int i=0; i     

同步发送

除了异步调用,LocalBroadcastManager还提供了同步调用的方法:

               1                      2                      3                      4                      5        
               public void sendBroadcastSync(Intent intent) {                      if (sendBroadcast(intent)) {                      executePendingBroadcasts();                      }                      }        

类比异步的发送广播方法,同步发送只是增加了在找到订阅记录之后,不等Handler被唤醒,直接在发送广播的线程直接执行了广播事件的处理,所以receiveronReceive方法可能执行在任意线程,决定权在于发送的是哪种广播,如果是异步广播,那么将在主线程执行,如果是同步广播,那么将会在发送广播所在的线程执行.

我们在构造LocalBroadcastManager的时候就已经看到HandlerhandlerMessage方法中只处理一个特定的message类型,就是用于唤醒消息处理的,发送广播的最后就是发送的这条消息。因为Handler使用的是Looper.mainLooper,所以最后receiveronReceive方法都是执行在主线程,所以要求我们不能在onReceive方法中完成太多的费时操作。

适用场景

分析完之后,我们就大概能想到LocalBroadcast的适用场景了,就是当我们想进行一些费时操作,所有把它扔在了异步线程去处理,当我们想知道它具体执行的进度的时候就可以用发送LocalBroadcast的方式实现,为什么不是直接使用Broadcast,因为Broadcast会涉及到Binder机制,必然效率会大打折扣,而且还有广播使用不当的话,还可能产生一些安全问题。可能还会有同学问,为什么我不是直接用Handler实现,类似AsyncTask,就是直接使用的Handler实现的线程间通信,没错,用Handler是可以满足需求,但是如果在一些特定场景下需要通知多个receiver,比如在后台下载更新包,下载完成以后每个页面都可能需要响应,这时候怎么处理呢?如果单纯使用Handler肯定是不能满足需求的,因为我们在分析Handler的时候就提到过,Handler发送的每条Message都只有一个target,那就是发送这条消息的那个Handler实例,所以它只会被处理一次。而使用LocalBroadcast就省心很多,每个页面都对目标action进行监听,这样下载成功的事件就能被所有的页面感知到。总之,因为LocalBroadcastHandler之上进行了一些封装,所以在对付特定需求时,用起来会更加得心应手,具体取舍,还是看需求,哪个用起来方便就用哪个。

对比EventBus

整篇文章,我们多次提到了EventBus,因为我们发现,他们的使用方式是如此相似,而且LocalBroadcast能实现的功能,用EventBus也能实现,比如我们继续讨论上面那个需求,用EventBus也能满足需求,而且使用上好像更方便一些,因为不用定义那么多BroadcastReceiver的子类,只需要在用到的地方加一个方法就行了。

如果联想到我们上篇文章对EventBus的分析,发现LocalBroadCastManager其实就是一个简化版的EventBus,它简化了两点:

  1. 限制Subscriber的类型,这样就省去了EventBusSubscriberMethodFinder这个重量级嘉宾,在LocalBroadCastManager中,所有的Subscriber都是BroadcastReceiver的子类,SubscriberMethod就是BroadcastReceiveronReceive()方法,这样在代码的简洁程度上,LocalBroadCastManager完胜EventBus,但是同时带来的缺点就是不灵活。
  2. 限制threadModeEventBus提供了四种threadMode,分别是MAINPOSTINGBACKGROUND以及ASYNCLocalBroadCastManager通过发送同步广播和异步广播,实现了主线程处理和发送事件线程处理两种模式,类比到EventBus,就是MAINPOSTING
    因为这两点限制,而且EventBus在使用上不仅不比LocalBroadCastManager复杂,而且还多了很多Feature,导致了LocalBroadCastManager只能闪在一边,没有任何登场的机会。

内存泄露

Handler、EventBus以及LocalBroadcastManager三者如果处理不当都有可能引发内存泄露,具体来说:Handler因为发送的每个Message对象有持有一个对发送者Handler的引用,方便到处理消息的时候调用handlerhandleMessage()方法,所以在把Handler当做内部类使用时,一定要使用static,不然因为内部类会持有外部类的引用,会导致外部类泄露,直到消息被处理并回收;EventBusLocalBroadCast就类似了,因为它们都会在注册的时候将订阅者保存在订阅者列表中,所以在不使用的时候一定要调用取消订阅方法将对象移除

在最后给一句话总结LocalBroadcastManager:披着广播外衣的Handler

更多相关文章

  1. Android客户端和服务器端数据交互的第四种方法
  2. MTK 添加宏控方法
  3. Android AsyncTask onProgressUpdate 方法的些许研究
  4. Android_Touch事件的分发和消费机制
  5. Android Studio中genymotion安装方法
  6. Android使用HttpClient方法和易错问题
  7. Android开机自启动程序设置及控制方法思路浅谈
  8. android获取当前运行Activity名字的方法
  9. android画任意曲线时,去除移动过程中出现莫名直线的方法

随机推荐

  1. 【Android翻译】Support Library Setup
  2. Android——自由拖动并显示文字的悬浮框
  3. Android启动图3秒后自动跳转首页
  4. Android(安卓)自定义View学习(4)波浪效果
  5. 通过Android(安卓)Studio查看SDK源码
  6. Android(安卓)中input event的分析
  7. android studio中使用NDK开发C++
  8. Android(安卓)4.1 Netd详细分析(五)代码分
  9. Android开发入门之访问通讯录中的联系人
  10. android-HttpClient和HttpURLConnection