组件化通信

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 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> 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 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的影响。

    你可能感兴趣的:(组件化通信)