• EventBus框架原理解析(结合源代码)(上)


    上一篇文章http://blog.csdn.net/crazy__chen/article/details/47425779

    和大家一起模仿EventBus的实现机制。和大家一起写出了一个简易的EventBus。

    通过这个项目,大家应该对EventBus的实现机理由大致的了解。

    建议大家在看这篇文章之前。先读上一篇文章,有助于了解。

    本篇文章希望通过对Github上EventBus的源代码。向大家讲些事实上现细节。

    网上有非常多讲EventBus架构的文章。我看了以后都认为不大清晰,希望这篇文章能够帮到你。

    EventBus源代码地址https://github.com/greenrobot/EventBus


    对于整个框架的理解,我们先来看一下文件图


    我们希望了解整个框架的每一个文件存在的意义,在这里我将具体介绍大部分主要类的用处和设计的思路。


    我们都知道整个框架的入口是EventBus这个类,网上非常多文章都是以这个类为入口

    我却先给大家将一些实体类的构建。

    我们知道,EventBus解耦的方式事实上是使用了反射。我在调用register()方法的时候,EventBus就扫描调用这种方法的类,将须要反射调用的方法信息记录下来,这些方法就是onEvent(),onEventMainThread(),onEventBackgroundThread()和onEventAsync()。

    这些方法被记录以后,假设有人调用post()方法,就会在记录里面查找,然后使用发射去触发这些方法

    反射触发方法的方式是调用Method类的invoke()方法,其签名例如以下:

    public Object invoke(Object obj, Object... args)
    显然要我们须要1,Method对象,订阅者对象obj。还有參数对象args

    也就是说记录里面,相应一个订阅方法。起码要有上面三个參数。


    OK,依据面向对象的思想,我们最好将这些參数封装成一个详细的类。接下来我们看一张图


    这张图说明了EventBus里面实体类的构造结构,外层的类包裹着里面的类。

    也就是外面的方框有一个里面方框所代表的类。作为它的属性

    我们能够看到PendingPost包括Subscription,Subscription包括SubscriberMethod,SubscriberMethod包括ThreadMode

    我如今向大家说明一下。

    首先,最里面的是SubscriberMethod类,它的意义就是EventBus扫描到的订阅方法主体

    final class SubscriberMethod {
        /**
         * 方法本体
         */
        final Method method;
        /**
         * 订阅类型
         */
        final ThreadMode threadMode;
        /**
         * 參数类型
         */
        final Class<?> eventType;

    这个类包括了EventBus扫描到的方法Method,订阅类型ThreadMode。事实上是一个枚举类。它代表订阅方法的类型(是在主线程,子线程,还是在公布线程执行等等)

    public enum ThreadMode {  
        PostThread,    
        MainThread, 
        BackgroundThread,
        Async
    }
    还有订阅方法的參数Class<?> eventType

    试想一下,要假设要反射调用这种方法。那么我们如今仅仅满足了第一个条件,就是我们获得了方法对象本身


    然后是Subscription。它的意义就是一个订阅。订阅包括订阅者和订阅方法

    final class Subscription {
        /**
         * 订阅者
         */
        final Object subscriber;
        /**
         * 订阅的方法
         */
        final SubscriberMethod subscriberMethod;
        /**
         * 优先级
         */
        final int priority;
        ......
    }
    能够见到,有订阅者。订阅方法对象SubscriberMethod,一个优先级。优先级是用于队列排序的,后面会讲到,优先级越高。当post的时候。者订阅就越先被触发。

    这时反射的第二个条件,反射要传入的第一个參数就具备了。



    再次封装成PendingPost,这个类的意义是在队列中排队的一个详细运行请求,也就是post以后,就会产生这样一个对象

    final class PendingPost {
        Object event;
        Subscription subscription;
        PendingPost next;
        .......
    }
    当中Object event,就是我们post(Object obj)方法调用的时候。參数的obj。这就是我们反射须要的最后一个參数。


    这样,我们通过构建这几个层次分明的实体类,我们就具备了触发反射的能力。

    通过上面的分析我们也知道。我们在register()的时候。目的是获得Subscription对象

    我们在post(Object obj)的时候,目的是将传入的obj和对应的Subscription对象一起封装成PendingPost对象

    将其放入队列里面排队,队列会不断取出元素。触发反射!


    了解上面思路以后,我们開始从EventBus入手。看看最基本的register和post方法,详细是再怎么做到的。

    再次之前。我们要看EventBus里面的几个重要属性,这几个属性非常复杂。大家先有个印象

    public class EventBus {
    
        /** Log tag, apps may override it. */
        public static String TAG = "Event";
    
        static volatile EventBus defaultInstance;
    
        private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
        /**
         * key为參数类型,value为參数类型及其父类型,接口类型列表
         */
        private static final Map<Class<?

    >, List<Class<?

    >>> eventTypesCache = new HashMap<Class<?

    >, List<Class<?>>>(); /** * key为參数类型,value为该參数类型的全部订阅(订阅包含,订阅者,订阅方法,优先级) */ private final Map<Class<?

    >, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; /** * key为订阅者,value为參数列表 */ private final Map<Object, List<Class<?

    >>> typesBySubscriber; /** * 能够看做缓存,key參数类型,value是參数 */ private final Map<Class<?>, Object> stickyEvents; ....... }


    这么多属性有什么意义?有的是为了功能必须,有的是为了提高性能,比如缓存。

    再看源代码过程中我会看到他们的作用。

    可是如今我们必须关注的一个属性是subscriptionsByEventType。这个属性我们在上一篇文章也说过它的重要作用。

    subscriptionsByEventType的key是參数类型,比如我们又一个onEvent(Info i)方法,我们知道EventBus是通过參数类型找到订阅方法的,比如post(new Info("msg"));

    EventBus负责找到所以订阅了Info类型的订阅方法。比如上面说的onEvent(Info i)

    那么这就要求我们将Info.class记录下来。作为key,通过这个key,我们就能够找到全部这些方法了。

    所以。subscriptionsByEventType的value是一个ArrayList,里面存储了Subscription。


    OK,如今让我们来看register()方法

    private synchronized void register(Object subscriber, boolean sticky, int priority) {
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
            for (SubscriberMethod subscriberMethod : subscriberMethods) {//遍历改订阅者的全部订阅方法
                subscribe(subscriber, subscriberMethod, sticky, priority);
            }
        }
    在这种方法里面,调用了subscriberMethodFinder这个类的findSubscriberMethods()方法。依据上面的分析我们也知道。这种方法用于找出订阅者subscriber中的全部订阅方法,而且将它们封装成SubscriberMethod对象返回。

    然后调用了subscribe()方法。将SubscriberMethod对象。和订阅者一起,封装成了subscription对象,我们详细看这两个方法。

    首先是findSubscriberMethods()

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
            String key = subscriberClass.getName();//订阅者名称
            List<SubscriberMethod> subscriberMethods;//订阅的方法集
            synchronized (methodCache) {
                subscriberMethods = methodCache.get(key);
            }
            if (subscriberMethods != null) {
                return subscriberMethods;
            }
            subscriberMethods = new ArrayList<SubscriberMethod>();
            Class<?

    > clazz = subscriberClass; /** * 记录订阅的全部方法,防止父类反复订阅在子类中已经订阅的方法 */ HashSet<String> eventTypesFound = new HashSet<String>(); StringBuilder methodKeyBuilder = new StringBuilder(); while (clazz != null) { String name = clazz.getName(); if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) { // Skip system classes, this just degrades performance break; } // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) Method[] methods = clazz.getDeclaredMethods();//找到订阅者的全部方法 for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {//仅仅找public方法。并且没有static,final等修饰 Class<?>[] parameterTypes = method.getParameterTypes();//方法參数 if (parameterTypes.length == 1) { String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length()); ThreadMode threadMode; if (modifierString.length() == 0) { threadMode = ThreadMode.PostThread; } else if (modifierString.equals("MainThread")) { threadMode = ThreadMode.MainThread; } else if (modifierString.equals("BackgroundThread")) { threadMode = ThreadMode.BackgroundThread; } else if (modifierString.equals("Async")) { threadMode = ThreadMode.Async; } else { if (skipMethodVerificationForClasses.containsKey(clazz)) {//过滤 continue; } else { throw new EventBusException("Illegal onEvent method, check for typos: " + method); } } Class<?> eventType = parameterTypes[0];//參数 methodKeyBuilder.setLength(0); methodKeyBuilder.append(methodName); methodKeyBuilder.append('>').append(eventType.getName()); String methodKey = methodKeyBuilder.toString(); if (eventTypesFound.add(methodKey)) {//假设之前没有加入 // Only add if not already found in a sub class subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType)); } } } else if (!skipMethodVerificationForClasses.containsKey(clazz)) { Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "." + methodName); } } } <pre name="code" class="java"> clazz = clazz.getSuperclass();//查找父类


    } if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " + ON_EVENT_METHOD_NAME); } else { synchronized (methodCache) { methodCache.put(key, subscriberMethods); } return subscriberMethods; } }

    
    
    上面的代码有点复杂,事实上就是扫描订阅者,找到订阅方法。

    注意有这样一个

     /**
             * 记录订阅的全部方法,防止父类反复订阅在子类中已经订阅的方法
             */
            HashSet<String> eventTypesFound = new HashSet<String>();
    这个eventTypesFound是记录了整个扫描过程中扫描到的方法签名。为什么要记录呢?

    看到这一句

    clazz = clazz.getSuperclass();//查找父类

    也就是说扫描一个订阅者的时候,假设这个订阅者本身没有订阅方法,可是它的父类有订阅方法,也会被扫描出来

    这样的优化就是为了实现继承的作用。

    所以扫描父类的时候,要避免反复记录子类中已经扫描到的订阅方法。


    另外另一点。就是skipMethodVerificationForClasses,它能够用于过滤不须要扫描的方法,也就是这些订阅不会被记录

    这个属性时在创建时传入的对象,我们看SubscriberMethodFinder的构造函数

    SubscriberMethodFinder(List<Class<?>> skipMethodVerificationForClassesList) {
            skipMethodVerificationForClasses = new ConcurrentHashMap<Class<?

    >, Class<?

    >>(); if (skipMethodVerificationForClassesList != null) { for (Class<?> clazz : skipMethodVerificationForClassesList) { skipMethodVerificationForClasses.put(clazz, clazz); } } }

    传入一个名单。名单内的订阅者将会被忽略。

    最后,在封装过程中,依据方法签名,构造SubscriberMethod,而且将SubscriberMethod加入到队列里面,最后返回这个队列。

    这样findSubscriberMethods()就结束了。


    我们接着register()方法里面的

    for (SubscriberMethod subscriberMethod : subscriberMethods) {//遍历改订阅者的全部订阅方法
                subscribe(subscriber, subscriberMethod, sticky, priority);
            }
    这里将方法封装成subscription

    // Must be called in synchronized block
        private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
            Class<?

    > eventType = subscriberMethod.eventType; CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);//封装成<span style="font-family: Arial, Helvetica, sans-serif;">newSubscription</span> if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<Subscription>(); subscriptionsByEventType.put(eventType, subscriptions);//假设该參数类型没有队列。新建一个 } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) // subscriberMethod.method.setAccessible(true); int size = subscriptions.size(); for (int i = 0; i <= size; i++) {//依据优先级找到在队列的位置 if (i == size || newSubscription.priority > subscriptions.get(i).priority) { subscriptions.add(i, newSubscription); break; } } List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);//这里是为了删除订阅的须要 if (subscribedEvents == null) { subscribedEvents = new ArrayList<Class<?>>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); if (sticky) {//是否粘性 if (eventInheritance) { // Existing sticky events of all subclasses of eventType have to be considered. // Note: Iterating over all events may be inefficient with lots of sticky events, // thus data structure should be changed to allow a more efficient lookup // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>). Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, Object> entry : entries) { Class<?> candidateEventType = entry.getKey(); if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue(); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { Object stickyEvent = stickyEvents.get(eventType); checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } }


    上面,依据首先封装出一个subscription。然后推断subscriptionsByEventType里面是否由參数类型相应的队列,没有就创建一个

    将subscription增加队列。

    然后就涉及到一个typesBySibcriber属性,看我凝视

    /**
         * key为订阅者,value为參数类型列表
         */
        private final Map<Object, List<Class<?>>> typesBySubscriber;
    也就是说这个属性能够告诉我们,某个订阅者。订阅了哪些參数类型

    通过这个记录。我们能够为某个订阅者解除订阅,看EventBus里面的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) { unubscribeByEventType(subscriber, eventType);//为该类型解除订阅 } typesBySubscriber.remove(subscriber); } else { Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }


    /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
        private void unubscribeByEventType(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中找到參数类型列表中的subscription队列,再在队列中找到订阅者subscriber,将它们从队列清除

    这样就实现了解除订阅。


    OK。subscribe()方法的最后,另一个关于sticky的推断,这个我们后面再讲,也就是subscribe()方法先说到这,register()方法也说到这。

    下一篇文章。我会继续讲post()方法的实现。

    假设对文章内容有不论什么疑惑,欢迎留言。

    转载请注明出处。

  • 相关阅读:
    003 All Primitives In Java
    002 JAVA Hello World!
    001 JAVA- 塑造可跟着你走的IDE
    input子系统
    ctargs使用
    内核中的锁
    GridView 使用详解
    Button 使用详解
    TextView 使用详解
    ImageView 使用详解
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7002158.html
Copyright © 2020-2023  润新知