EventBus源码分析(一)

简介

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.
     * 

* 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 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, List> METHOD_CACHE = new ConcurrentHashMap<>();
    
    List findSubscriberMethods(Class subscriberClass) {
        //1.先从缓存获取该订阅者下的所有订阅方法,获取到的话直接返回
        List 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 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 subscriberMethods = new ArrayList<>();
        //map集合,key为EventType,value为订阅者的某一个方法
        final Map anyMethodByEventType = new HashMap<>();
        //map集合,key为订阅方法名等参数拼接起来的string,value为方法的getDeclaringClass()
        final Map 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,接下来还需要给每个方法添加订阅,subscribe(subscriber, subscriberMethod)方法又是怎样的呢?

  public void register(Object subscriber) {
        Class subscriberClass = subscriber.getClass();
        List 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 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> 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> 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 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源码分析(一))