组件化通信

LocalBroadcastManager

  • 单例获取LocalBroadcastManager实体,dooublecheck

    LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this)
    
  • 注册广播接收者,

    lbm.registerReceiver(mrevicer, new    IntentFilter("LOCAL_ACTION"));
    

    构建一个广播信息实体ReceiverRecord entry = new ReceiverRecord(filter, receiver)到ArrayList中
    最后action的string做key,包含ReceiverRecord 的ArrayList做value放入HashMap mActions 中

    mActions.put(action, entries)
    
  • 发生广播

    lbm.sendBroadcast(new Intent().setAction("LOCAL_ACTION").putExtra("text","ni shi local"));
    

    从mActions中获取广播匹配"LOCAL_ACTION"的实体ReceiverRecord的集合
    循环集合得到ReceiverRecord对每个进行math,把match的结果放入集合mPendingBroadcasts,然后发生message
    内部类Handler处理消息调用executePendingBroadcasts()
    从集合mPendingBroadcasts中拿到每个receiver调用receiver.onReceive(mAppContext, br.intent)

    filter.match(action, type, scheme, data,
            categories, "LocalBroadcastManager")
    
  • 取消广播
    从mActions中取出对应action的集合
    然后有当前对象,就从集合中移除

    lbm.unregisterReceiver(mrevicer);
    

优点比全局广播效率高,
缺点,使用Android原生API,不适合javalib,LocalBroadcastManager的传递不方便需要context不是所有module都有context对象,组件化能用但是不适合

EventBus

EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment,Activity,Service线程之间传递数据,只是传递。Handler是很多时候逃不掉的坑。

register

通过getDefault获取EventBus实例,这是一条默认的事件总线,通过单例模式实现,其构造函数是Public的,也可以通过EventBus.builder().build()来创建多条事件总线,一般在oncreate中创建。

EventBus.getDefault().register(this);
//这是一个构建者模式+链式调用,build之前可以链式调用添加参数
EventBus.builder().build()
unregister

如果是默认直接通过下面的方法取消,自己新建的就要拿到之前保留的对象,然后调用unregister方法

EventBus.getDefault().unregister(this);
post

发送消息,可以是任意对象。

EventBus.getDefault().post(daLaBa);
Subscribe
  1. threadMode
    • POSTING
      默认的模式,开销最小的模式,因为声明为POSTING的订阅者会在发布的同一个线程调用,发布者在主线程那么订阅者也就在主线程,反之亦,避免了线程切换,如果不确定是否有耗时操作,谨慎使用,因为可能是在主线程发布

    • MAIN
      主线程调用,视发布线程不同处理不同,如果发布者在主线程那么直接调用(非阻塞式),如果发布者不在主线程那么阻塞式调用

    • MAIN_ORDERED
      和MAIN差不多,主线程调用,和MAIN不同的是他保证了post是非阻塞式的(默认走MAIN的非主线程的逻辑,所以可以做到非阻塞)

    • BACKGROUND
      在子线程调用,如果发布在子线程那么直接在发布线程调用,如果发布在主线程那么将开启一个子线程来调用,这个子线程是阻塞式的,按顺序交付所有事件,所以也不适合做耗时任务,因为多个事件共用这一个后台线程

    • ASYNC
      在子线程调用,总是开启一个新的线程来调用,适用于做耗时任务,比如数据库操作,网络请求等,不适合做计算任务,会导致开启大量线程

流程
register
  1. 获得传入对象的类对象。

  2. 获得这个类中定义的订阅函数。
    subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    //subscriberClass传入对象的class

    • 从缓存中取出METHOD_CACHE订阅方法集合。如果有直接返回。

      List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
      
    • 如果缓存中没有,选择合适的方法寻找订阅方法。
      subscriberMethods = findUsingReflection(subscriberClass);直接通过反射查找
      subscriberMethods = findUsingInfo(subscriberClass);默认查找
      两者区别第二个多出从subscriberMethodFinder.subscriberInfoIndexes中获取SubscriberInfoIndex来获取SubscriberInfo

      1. 从状态池获得一个状态对象。
        FindState findState = prepareFindState

      2. 将订阅者类传入,设置一些状态对象的参数。
        findState.initForSubscriber(subscriberClass);

      3. 通过状态对象寻找订阅者信息,如果找到就获取到了订阅方法数组,并循环检测合法性,通过findState.subscriberMethods.add(subscriberMethod)放入其集合中。

        // 寻找方法findState.subscriberInfo = getSubscriberInfo(findState);
        // 这一步就是多出来的通过subscriberMethodFinder.subscriberInfoIndexes的索引缓存查找
        
      4. 如果没有没有找到订阅者信息,则调用方法,用反射去寻找订阅方法。
        findUsingReflectionInSingleClass(findState);

        • 获取订阅类中定义的方法。(方法类型、方法参数长度之类)
        • 遍历方法集合,检测完合法性,将合法的订阅方法封装,存入状态对象
        Class<?> eventType = parameterTypes[0]
        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                              subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
        
      5. 状态对象指向父类,再次循环,找父类中的订阅方法。
        findState.moveToSuperclass();

      6. 返回subscriberMethods,并且回收findState

        return getMethodsAndRelease(findState)
        
    • 如果没有找到订阅方法,抛出异常程序崩溃。
      如果找到了订阅方法,将它和订阅者类对象组成键值对存储到缓存METHOD_CACHE。

      METHOD_CACHE.put(subscriberClass, subscriberMethods);
      
  3. 对每个订阅函数进行订阅操作。

    • 根据订阅方法获取事件类型。

      Class<?> eventType = subscriberMethod.eventType;
      
    • 将订阅者和订阅方法组合成一个订阅对象。

      Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
      
    • 获取订阅对象集合,如果为空直接新建集合后放入,
      将订阅对象集合中对象按优先级排序后

      if (subscriptions == null) {
          subscriptions = new CopyOnWriteArrayList<>();
          subscriptionsByEventType.put(eventType, subscriptions);
      } else {
          if (subscriptions.contains(newSubscription)) {
          throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);
          }
      }
      
      int size = subscriptions.size();
      for (int i = 0; i <= size; i++) {
          if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
              subscriptions.add(i, newSubscription);
              break;
          }
      }
      
    • 根据注册方法传入的对象获取到它可以相应的事件类型集合,
      如果没有新建一个集合,将对象做key,集合做value存Map typesBySubscriber当中。
      最后都会将事件类型存入集合

       //根据订阅者获取事件类型集合
      List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
          //没有存储过事件类型,新建一个集合
          if (subscribedEvents == null) {
              subscribedEvents = new ArrayList<>();
              typesBySubscriber.put(subscriber, subscribedEvents);
        }
      //put当前事件类型进去
      subscribedEvents.add(eventType);
      
    • 处理粘性事件。如果之前有粘性事件,则取出并发送给订阅者

post

使用ThreadLocal来存储事件,他可以隔离多个线程对数据的访问冲突

  1. 获得post线程状态对象。

    PostingThreadState postingState = currentPostingThreadState.get();
    
  2. 从这个线程状态对象中取得事件队列。加入事件对象。

    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    
  3. 如果事件没有在分发中,

    • 设置分发状态和是否主线程。

    • 循环取出事件队列中的事件进行分发过程。

      while (!eventQueue.isEmpty()) {
          postSingleEvent(eventQueue.remove(0), postingState);
      }
      

      找到我们自定义事件类型的类对象。
      --->eventInheritance若为true,会找到这个事件类型的自身和父类,逐一进行事件分发。
      --->eventInheritance为true,只对自身事件类型进行分发。
      --->最终都会通过下面的postSingleEventForEventType分发

      subscriptionFound = postSingleEventForEventType(event, postingState, eventClass)
      
      1. 根据事件类型获得订阅对象集合。

        1. subscriptions = subscriptionsByEventType.get(eventClass);
        
      2. 如果集合不为空,循环subscriptions集合

        将event和subscription给赋值postingState的属性

        调用postToSubscription(subscription, event, postingState.isMainThread);

        赋值aborted = postingState.canceled;

        然后重置postingState

        postingState.event = null;
        postingState.subscription = null;
        postingState.canceled = false;
        

        如果aborted = ture跳槽循环

        • 根据threadMode找到合适的线程来调用订阅函数而已

          private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
                //根据订阅方法注解中标明的执行线程执行
                  switch (subscription.subscriberMethod.threadMode) {
                      case POSTING:
                          invokeSubscriber(subscription, event);
                          break;
                      case MAIN:
                          if (isMainThread) {
                              invokeSubscriber(subscription, event);
                          } else {
                              mainThreadPoster.enqueue(subscription, event);
                          }
                          break;
                      case BACKGROUND:
                          if (isMainThread) {
                              backgroundPoster.enqueue(subscription, event);
                          } else {
                              invokeSubscriber(subscription, event);
                          }
                          break;
                      case ASYNC:
                          asyncPoster.enqueue(subscription, event);
                          break;
                      default:
                          throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
                  }
             }
          

          **主线程mainThread: **

          Poster:通过继承Handler重写handleMessage增加新的enqueue方法

          enqueue方法:加入自己的队列,发送空消息

          handleMessage:invokeSubscriber(subscription, event)省略了很多逻辑

          **backgroundPoster: **

          enqueue方法:加入自己的队列,如果第一次启动线程就传入自己(实现了Runnable)然后启动线程

          run方法:无限循环,从队列取pendingPost,然后调用eventBus.invokeSubscriber(pendingPost)

          **asyncPoster: **

          enqueue方法:加入自己的队列,启动线程传入自己(实现了Runnable)

          run方法:从队列取pendingPost,然后调用eventBus.invokeSubscriber(pendingPost)

          eventBus.invokeSubscriber(pendingPost):

          method.invoke(subscription.subscriber, event):

      3. 返回flase

      4. 通过postToSubscription向订阅者进行事件分发。
        接下来调用postToSubscription继续实际分发。

      如果都没有就发送没有订阅者的事件post(new NoSubscriberEvent(this, event));

  • 重置postingState

    postingState.isPosting = false;
    postingState.isMainThread = false; 
    
unregister

根据传入对象找到订阅的事件类型集合,
for循环事件集合
从subscriptionsByEventType获取subscriptions集合
如何subscription.subscriber == subscriber(传入的对象)就从subscriptions集合移出

Rxbus

是基于Rxjava的一种封装要比EventBus好,因为效率高,线程调度和链式优于Eventbus

ModuleBus

跟Eventbus类似,能传递基础数据类型。自定义的事件信息模型还是要添加到BaseModule中才能让其他模块引用

ModularizationArchitecture

是一个路由带事件功能,已经停工了,需要context,使用场景有限,不多说。

LocalRouter.getInstance()

组件化中使用总线怎么办

可以抽离成一个单独的模块,让BaseModule依赖,把事件抽离到XXXBusModule,来解耦,减少对BaseModule的影响。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,812评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,626评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,144评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,052评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,925评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,035评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,461评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,150评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,413评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,501评论 2 307
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,277评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,159评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,528评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,868评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,143评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,407评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,615评论 2 335

推荐阅读更多精彩内容

  • 项目到了一定阶段会出现一种甜蜜的负担:业务的不断发展与人员的流动性越来越大,代码维护与测试回归流程越来越繁琐。这个...
    fdacc6a1e764阅读 3,147评论 0 6
  • EventBus基本使用 EventBus基于观察者模式的Android事件分发总线。 从这个图可以看出,Even...
    顾氏名清明阅读 612评论 0 1
  • EventBus源码分析 Android开发中我们最常用到的可以说就是EventBus了,今天我们来深入研究一下E...
    BlackFlag阅读 504评论 3 4
  • EventBus用法及解析 EventBus介绍: EventBus主要是用来组件之间进行信息传递的,相对于接口回...
    111_222阅读 535评论 0 1
  • 先吐槽一下博客园的MarkDown编辑器,推出的时候还很高兴博客园支持MarkDown了,试用了下发现支持不完善就...
    Ten_Minutes阅读 555评论 0 2