美文网首页
EventBus源码分析(一)

EventBus源码分析(一)

作者: CKTim | 来源:发表于2020-06-24 23:44 被阅读0次

    简介

    EventBus是针对Android优化的发布-订阅事件总线,简化了Android组件间的通信。EventBus以其简单易懂、优雅、开销小等优点而备受欢迎,基本在每一个APP都会用到,Eventbus源码较为简单,可作为开源框架源码学习入门教材

    如何使用

    gradle中添加依赖:

    implementation 'org.greenrobot:eventbus:3.2.0'
    

    在需要发布事件的组件里调用如下代码即可实现事件发送

    EventBus.getDefault().post(TestEvent())
    

    在需要订阅事件的页面进行注册,并添加相应@Subscribe注解即可实现事件订阅

        override fun onStart() {
            super.onStart()
            EventBus.getDefault().register(this)
        }
    
        override fun onStop() {
            super.onStop()
            EventBus.getDefault().unregister(this)
        }
    
        @Subscribe(threadMode = ThreadMode.MAIN)
        public fun onRecEvent(event:TestEvent) {
            Log.e(TAG, "收到事件:$event")
        }
    

    源码分析(register和unregister)

    EventBus.getDefault() 由字面感觉是个单例,点进去看看:
        static volatile EventBus defaultInstance;
        /** Convenience singleton for apps using a process-wide EventBus instance. */
        //可以看到是采用双重锁的一个单例模式
        public static EventBus getDefault() {
            EventBus instance = defaultInstance;
            if (instance == null) {
                synchronized (EventBus.class) {
                    instance = EventBus.defaultInstance;
                    if (instance == null) {
                        instance = EventBus.defaultInstance = new EventBus();
                    }
                }
            }
            return instance;
        }
    
    
    register注册
        /**
         * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
         * are no longer interested in receiving events.
         * <p/>
         * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
         * The {@link Subscribe} annotation also allows configuration like {@link
         * ThreadMode} and priority.
         */
        public void register(Object subscriber) {
            //获取订阅者对象类型
            Class<?> subscriberClass = subscriber.getClass();
            //获取该订阅者的所有订阅方法(即被打上 @Subscribe()注解的方法)
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            //上锁
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    //遍历逐一订阅每一个方法
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    

    上述流程,第一步先是获取了subscriber的对象类型,然后通过findSubscriberMethods()方法获取了该subscriber的所有订阅方法,并将每个方法包装为SubscriberMethod对象,最后遍历逐一调用subscribe()去订阅每一个方法。

    这里看一下findSubscriberMethods()是如何查找订阅者所订阅的方法并将其包装为SubscriberMethod返回的:

        //缓存map,key是订阅者对象类型,value是该订阅者下的所有订阅方法
        private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
        
        List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
            //1.先从缓存获取该订阅者下的所有订阅方法,获取到的话直接返回
            List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
            if (subscriberMethods != null) {
                return subscriberMethods;
            }
            //是否忽略注解器生成的MyEventBusIndex
            //默认ignoreGeneratedIndex为false,即忽略,走findUsingInfo分支
            if (ignoreGeneratedIndex) {
                subscriberMethods = findUsingReflection(subscriberClass);
            } else {
                subscriberMethods = findUsingInfo(subscriberClass);
            }
            //找不到@Subscribe()注解的方法,抛出异常,这就是平时调用EventBus.getDefault().register(this)然后没有注册接收事件闪退的原因
            if (subscriberMethods.isEmpty()) {
                throw new EventBusException("Subscriber " + subscriberClass
                        + " and its super classes have no public methods with the @Subscribe annotation");
            } else {
                //查找成功后放入缓存,下次直接用
                METHOD_CACHE.put(subscriberClass, subscriberMethods);
                return subscriberMethods;
            }
        }
    

    ignoreGeneratedIndex不设置的话默认为false,所以这里直接走到了findUsingInfo()方法,看一下findUsingInfo()具体是怎么查找订阅方法的:

        private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
            //这里通过FindState来储存找到的方法信息
            FindState findState = prepareFindState();
            findState.initForSubscriber(subscriberClass);
            // 循环,从当前类开始遍历该类的所有父类
            while (findState.clazz != null) {
                //获取订阅者的信息,因为没有默认没有使用MyEventBusIndex,这里获取到会是null,走findUsingReflectionInSingleClass分支
                findState.subscriberInfo = getSubscriberInfo(findState);
                if (findState.subscriberInfo != null) {
                    //先无视掉。。
                    SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                    for (SubscriberMethod subscriberMethod : array) {
                        if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                            findState.subscriberMethods.add(subscriberMethod);
                        }
                    }
                } else {
                    //正常没有使用MyEventBusIndex来到这里
                    findUsingReflectionInSingleClass(findState);
                }
                // 将findState.clazz设置为当前的findState.clazz的父类
                findState.moveToSuperclass();
            }
            //获取methodsList并释放资源
            return getMethodsAndRelease(findState);
        }
        
        static class FindState {
            //某个订阅者下的所有订阅方法(这个list就是我们最终要拿到返回回去到第一步的)
            final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
            //map集合,key为EventType,value为订阅者的某一个方法
            final Map<Class, Object> anyMethodByEventType = new HashMap<>();
            //map集合,key为订阅方法名等参数拼接起来的string,value为方法的getDeclaringClass()
            final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
            final StringBuilder methodKeyBuilder = new StringBuilder(128);
    
            Class<?> subscriberClass;
            Class<?> clazz;
            boolean skipSuperClasses;
            SubscriberInfo subscriberInfo;
    
            void initForSubscriber(Class<?> subscriberClass) {
                this.subscriberClass = clazz = subscriberClass;
                skipSuperClasses = false;
                subscriberInfo = null;
            }
    
            //省略
            .....
        }
    

    上一步findUsingInfo()主要是走到了findUsingReflectionInSingleClass()这个方法里,那他又是如何查找订阅方法的呢。单看命名,翻译过来好像是“使用反射查找”???,即使用反射从订阅者中得到订阅方法??看一下代码:

      private void findUsingReflectionInSingleClass(FindState findState) {
            Method[] methods;
            try {
                // This is faster than getMethods, especially when subscribers are fat classes like Activities
                // 获取该类中声明的所有方法
                methods = findState.clazz.getDeclaredMethods();
            } catch (Throwable th) {
                // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
                try {
                    methods = findState.clazz.getMethods();
                } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
                    String msg = "Could not inspect methods of " + findState.clazz.getName();
                    if (ignoreGeneratedIndex) {
                        msg += ". Please consider using EventBus annotation processor to avoid reflection.";
                    } else {
                        msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
                    }
                    throw new EventBusException(msg, error);
                }
                findState.skipSuperClasses = true;
            }
            //遍历找到的方法数组
            for (Method method : methods) {
                //获取方法声明的修饰符,如private,public,protected等
                int modifiers = method.getModifiers();
                //进行位运算,取出用public修饰的方法,这就是为什么我们声明接收事件必须要使用public的原因
                if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                    //获取该方法下的参数类型
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    //只处理一个参数的方法
                    if (parameterTypes.length == 1) {
                        //获取该方法下的注解,用来从注解中获取声明的信息
                        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                        if (subscribeAnnotation != null) {
                             // 获取该方法的第一个参数对象
                            Class<?> eventType = parameterTypes[0];
                            // 检查是否需要添加进findState的订阅方法列表,后面看怎么较验
                            if (findState.checkAdd(method, eventType)) {
                                //获取注解的threadMode 信息,即我们常使用的@Subscribe(threadMode = ThreadMode.MAIN)
                                ThreadMode threadMode = subscribeAnnotation.threadMode();
                                //添加进订阅方法列表
                                findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                        subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                            }
                        }
                    } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                        String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                        throw new EventBusException("@Subscribe method " + methodName +
                                "must have exactly 1 parameter but has " + parameterTypes.length);
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException(methodName +
                            " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
                }
            }
        }
    

    由上面代码可以知道,findUsingReflectionInSingleClass()确实是使用了很多反射方法,第一步先调用getDeclaredMethods获取了Method[]数组,然后遍历该方法数组,取出用public修饰符修饰且只有单个参数的方法,紧接着取出该方法下的注解(必须是Subscribe类型的注解),然后通过调用findState.checkAdd(method, eventType)方法,判断是否需要将该方法添加进findState.subscriberMethods数组中,这个subscriberMethods数组也就是我们最终要返回回第一步的数据源。

    看一下findState.checkAdd(method, eventType)方法是如何判断是如何较验这个订阅方法是否有效的

    static class FindState {
            //省略
            .....
            boolean checkAdd(Method method, Class<?> eventType) {
                // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
                // Usually a subscriber doesn't have methods listening to the same event type.
                //map集合,key为EventType,value为订阅者的某一个method方法
                Object existing = anyMethodByEventType.put(eventType, method);
                if (existing == null) {
                    //之前没有其他方法订阅了该事件类型,返回true,表示需要添加进subscriberMethods数组
                    return true;
                } else {
                    if (existing instanceof Method) {
                        //之前已经有方法订阅过该事件类型,例如void1 void2都订阅接受String类型,则此时会走到这个分支,进行二次较验
                        if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                            // Paranoia check
                            throw new IllegalStateException();
                        }
                        // Put any non-Method object to "consume" the existing Method
                        //放进map
                        anyMethodByEventType.put(eventType, this);
                    }
                    return checkAddWithMethodSignature(method, eventType);
                }
            }
             
            //二次较验
            private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
                //key为方法名+"<"+事件类型
                methodKeyBuilder.setLength(0);
                methodKeyBuilder.append(method.getName());
                methodKeyBuilder.append('>').append(eventType.getName());
    
                String methodKey = methodKeyBuilder.toString();
                Class<?> methodClass = method.getDeclaringClass();
                Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
                if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                    // Only add if not already found in a sub class
                    // 首次注册methodClassOld为null,符合条件,之后methodClassOld不为空
                    //使用methodClassOld.isAssignableFrom(methodClass)判断methodClassOld是否为methodClass的父类,然后之前了解到我们是从子类开始遍历到父类的,
                    //显然这个条件不满足,这也是使用EventBus的时候子类不能重写父类的函数原因??????
                    return true;
                } else {
                    // Revert the put, old class is further down the class hierarchy
                    subscriberClassByMethodKey.put(methodKey, methodClassOld);
                    return false;
                }
            }
    
            //省略
            .....
        }
    

    到这里获取订阅方法的流程就已经结束了,获取到的订阅方法已经被打包成 SubscriberMethod对象并添加到findState.subscriberMethods数组里面去了,接下来只要把它取出来就好了。

    回到我们刚开始分析注册进来的地方,我们在一系列操作后拿到了订阅者所订阅的所有方法List<SubscriberMethod>,接下来还需要给每个方法添加订阅,subscribe(subscriber, subscriberMethod)方法又是怎样的呢?

      public void register(Object subscriber) {
            Class<?> subscriberClass = subscriber.getClass();
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    

    subscribe(subscriber, subscriberMethod)代码如下:

       // Must be called in synchronized block
        private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            //获取该订阅方法的订阅类型
            Class<?> eventType = subscriberMethod.eventType;
            //通过订阅者和订阅方法构建Subscription 对象
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            //通过事件类型,获取该事件类型下的所有订阅事件
            CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions == null) {
                subscriptions = new CopyOnWriteArrayList<>();
                //放入map缓存
                subscriptionsByEventType.put(eventType, subscriptions);
            } else {
                //如果重复订阅则会报错,这也是为什么有时我们多次调用 EventBus.getDefault().register(this)闪退的原因
                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;
                }
            }
            //将该订阅方法存放到typesBySubscriber中
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
            subscribedEvents.add(eventType);
            ...省略粘性事件相关代码
        }
    

    简单讲,订阅流程就是将获取到的subscriberMethod数组逐一添加到subscriptionsByEventType和typesBySubscriber这两个map中去,为了给在事件发送的时候使用,以此形成通信。

    这里比较复杂的就是获取方法的流程,简单总结调用顺序如下:
    findSubscriberMethods()->findUsingInfo()->包装成FindState对象存储相关信息和映射关系->findUsingReflectionInSingleClass()

    unregister解注册
    /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
    
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
            // 找到与事件类型相匹配的订阅方法
            List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions != null) {
                int size = subscriptions.size();
                for (int i = 0; i < size; i++) {
                    Subscription subscription = subscriptions.get(i);
                    // 将订阅者中的订阅方法移除
                    if (subscription.subscriber == subscriber) {
                        subscription.active = false;
                        subscriptions.remove(i);
                        i--;
                        size--;
                    }
                }
            }
        }
    

    取消订阅的方法中跟订阅反着来,subscriptionsByEventType和typesBySubscriber有没有很熟悉!!!首先通过获取typesBySubscriber map获取该订阅者所订阅的所有订阅类型,接着通过subscriptionsByEventType map获取每一个订阅类型下的所有属于该订阅者的订阅方法,然后逐一移除掉。

    以上就是订阅/取消订阅的流程,关于订阅后是如何接受到事件的,EventBus.getDefault().post(TestEvent())是如何发送给订阅者的,下一篇讲。。

    下一篇地址:https://www.jianshu.com/p/4792c5a30c30

    相关文章

      网友评论

          本文标题:EventBus源码分析(一)

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