美文网首页
Android EventBus源码解析(下)

Android EventBus源码解析(下)

作者: Android程序员老鸦 | 来源:发表于2021-05-27 17:54 被阅读0次

    上篇分析了EventBus查找所有的订阅集合List<SubscriberMethod>的过程,这篇继续往下走,分析一下订阅的流程:

     public void register(Object subscriber) {
    
            Class<?> subscriberClass = subscriber.getClass();
            //通过类名也就是activity的类查找List<SubscriberMethod>集合
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            //开始循环订阅每个方法
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            //获取订阅方法的入参类型,也即是事件类名
            Class<?> eventType = subscriberMethod.eventType;
            //又搞了个封装类Subscription,封装了事件订阅所在的类名和订阅的方法信息
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            //subscriptionsByEventType的格式是Map<Class<?>, CopyOnWriteArrayList<Subscription>>
            //缓存用的map,存放的是事件类型和Subscription集合的键值对
            CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions == null) {
                //为空则新建了一个空的subscriptions,并把当前的事件类型和这个subscriptions存起来
                subscriptions = new CopyOnWriteArrayList<>();
                subscriptionsByEventType.put(eventType, subscriptions);
            } else {
                //同一个方法订阅了两次,报错
                if (subscriptions.contains(newSubscription)) {
                    throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                            + eventType);
                }
            }
            //根据优先级排序装入subscriptions
            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;
                }
            }
            //又是个缓存map,typesBySubscriber的格式是Map<Object, List<Class<?>>>,存放的是订阅所在类名
            //和订阅事件类型集合的键值对,简单来说就是通过这个map可以方便拿到该类下所有的订阅事件类型
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
                //放入缓存
            subscribedEvents.add(eventType);
            //省略干扰代码。。。
        }
    

    看下来似乎就是把找到的订阅事件信息整理放在了两个map上:

    //存放的是事件类型和Subscription集合的键值对
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    //存放的是订阅所在类名和订阅事件类型集合的键值对
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    

    猜测一波,后续post发送事件后肯定就是对这两个map进行操作了
    接下来看看post(event)方法:

       //推送流程1
        public void post(Object event) {
            //currentPostingThreadState是一个ThreadLocal类型,这是java提供的一个线程内部的存储类,
            // 可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据,这里不详细分析
            PostingThreadState postingState = currentPostingThreadState.get();
            List<Object> eventQueue = postingState.eventQueue;
            //把事件放在队列里
            eventQueue.add(event);
            //判断一下状态
            if (!postingState.isPosting) {
                postingState.isMainThread = isMainThread();
                postingState.isPosting = true;
                if (postingState.canceled) {
                    throw new EventBusException("Internal error. Abort state was not reset");
                }
                try {
                  //一个一个顺序推送
                    while (!eventQueue.isEmpty()) {
                        //重点代码在这里,接着看这个方法
                        postSingleEvent(eventQueue.remove(0), postingState);
                    }
                } finally {
                    postingState.isPosting = false;
                    postingState.isMainThread = false;
                }
            }
        }
        //推送流程2
        private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
            Class<?> eventClass = event.getClass();
            boolean subscriptionFound = false;
            //eventInheritance是初始化eventBus的一个值,大意是是否一并通知该订阅事件的父类或实现的接口类的订阅事件,默认是true
            //比如 A extends B implements C  发布者post(A),那么找订阅者的时候不仅要找订阅了事件A的订阅者
            // 还要找订阅了B和C的订阅者
            if (eventInheritance) {
                //把订阅事件父类的订阅事件信息也找出来
                List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
                int countTypes = eventTypes.size();
                for (int h = 0; h < countTypes; h++) {
                    Class<?> clazz = eventTypes.get(h);
                    //|=是个或运算,自己和等式右边做或运算
                    subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
                }
            } else {
                //简单点理解可以看这里,上面的分支也最终走到了postSingleEventForEventType()方法
                subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
            }
            if (!subscriptionFound) {
                if (logNoSubscriberMessages) {
                    logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
                }
                if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                        eventClass != SubscriberExceptionEvent.class) {
                    post(new NoSubscriberEvent(this, event));
                }
            }
        }
        //推送流程3
        private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                //开始用到subscriptionsByEventType这个map了,通过事件类型取出所有订阅了该事件的方法信息集合
                subscriptions = subscriptionsByEventType.get(eventClass);
            }
            //简单判空处理
            if (subscriptions != null && !subscriptions.isEmpty()) {
                //循环遍历这个list
                for (Subscription subscription : subscriptions) {
                    //在线程存储对象里记录下发送的事件对象和订阅了该事件的信息
                    postingState.event = event;
                    postingState.subscription = subscription;
                    boolean aborted;
                    try {
                        //最终调用到这个方法,从方法名也可以看出是推送到订阅方的意思,继续走下去
                        postToSubscription(subscription, event, postingState.isMainThread);
                        aborted = postingState.canceled;
                    } finally {
                        postingState.event = null;
                        postingState.subscription = null;
                        postingState.canceled = false;
                    }
                    if (aborted) {
                        break;
                    }
                }
                return true;
            }
            return false;
        }
    //推送流程4 按照订阅时设置的调用线程分别处理
     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 MAIN_ORDERED:
                    if (mainThreadPoster != null) {
                        mainThreadPoster.enqueue(subscription, event);
                    } else {
                        // temporary: technically not correct as poster not decoupled from subscriber
                        invokeSubscriber(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);
            }
        }
    

    最后post事件后会到invokeSubScriber()方法,enqueue()方法只是做个异步调用最后还是会走到invokeSubScriber(),看方法名就知道是用到了反射去执行订阅方法:

     void invokeSubscriber(Subscription subscription, Object event) {
            try {
             //用subscription保存到的方法实例invoke()一下,最终完成了用事件推送来达到订阅方相应的目的
                subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
            } catch (InvocationTargetException e) {
                handleSubscriberException(subscription, event, e.getCause());
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Unexpected exception", e);
            }
        }
    

    至此,从订阅到post事件的流程讲完了,主要核心是缓存的设计,APT和反射的运用。最后,还有一个map在post过程中好像没被用到就是:

    //存放的是订阅所在类名和订阅事件类型集合的键值对
    private final Map<Object, List<Class<?>>> typesBySubscriber
    

    看他的键值对设计想必应该是用在判断/解除注册里可以方便的查找,看了看源码发现果然是:

    //用来判断该类是否注册过
     public synchronized boolean isRegistered(Object subscriber) {
            return typesBySubscriber.containsKey(subscriber);
        }
     //解除注册
        public synchronized void unregister(Object subscriber) {
            List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
            if (subscribedTypes != null) {
                for (Class<?> eventType : subscribedTypes) {
                    //相应的在subscriptionsByEventType这个map里也会移除调订阅信息
                    unsubscribeByEventType(subscriber, eventType);
                }
                typesBySubscriber.remove(subscriber);
            } else {
                logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
            }
        }
    

    OK,分析完成!看完源码后只是把主干流程梳理了一下,EventBus作为优秀的开源项目,其设计思想还是很值得反复研究的,多看看这种成熟优秀的项目对于自己的代码能力也是有很大的提升!

    相关文章

      网友评论

          本文标题:Android EventBus源码解析(下)

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