组件化通信

作者: ArcherZang | 来源:发表于2020-03-28 16:38 被阅读0次

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

    相关文章

      网友评论

        本文标题:组件化通信

        本文链接:https://www.haomeiwen.com/subject/zuksuhtx.html