EventBus源码分析+面试总结

概述

EventBus可以代替Android传统的Intent、Handler、Broadcast或接口函数,在Fragment、Activity、Service线程之间传递数据。具体使用方式参考github,本文我们将通过源码看下他是如何注册和发布事件的。

GitHub地址:https://github.com/greenrobot/EventBus

1.获取实例

//是一个“双重校验锁”的单例模式。
  public static EventBus getDefault() {
        if (defaultInstance == null) {
            Class var0 = EventBus.class;
            synchronized(EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }

        return defaultInstance;
    }
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        this.currentPostingThreadState = new ThreadLocal<EventBus.PostingThreadState>() {
            protected EventBus.PostingThreadState initialValue() {
                return new EventBus.PostingThreadState();
            }
        };
        this.logger = builder.getLogger();
        this.subscriptionsByEventType = new HashMap();
        this.typesBySubscriber = new HashMap();
        this.stickyEvents = new ConcurrentHashMap();
        this.mainThreadSupport = builder.getMainThreadSupport();
        this.mainThreadPoster = this.mainThreadSupport != null ? this.mainThreadSupport.createPoster(this) : null;
        this.backgroundPoster = new BackgroundPoster(this);
        this.asyncPoster = new AsyncPoster(this);
        this.indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        this.subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        this.logSubscriberExceptions = builder.logSubscriberExceptions;
        this.logNoSubscriberMessages = builder.logNoSubscriberMessages;
        this.sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        this.sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        this.throwSubscriberException = builder.throwSubscriberException;
        this.eventInheritance = builder.eventInheritance;
        this.executorService = builder.executorService;
    }

2.注册事件

调用注册方法:

EventBus.getDefault().register(this);

当调用EventBus实例的register方法的时候,会执行下面的逻辑:

    public void register(Object subscriber) {
     	//获取注册的对象的类型
        Class<?> subscriberClass = subscriber.getClass();
        // 然后获取注册的对象的订阅方法
        List<SubscriberMethod> subscriberMethods = this.subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized(this) {
            Iterator var5 = subscriberMethods.iterator();
			// 对每个订阅方法进行注册
            while(var5.hasNext()) {
                SubscriberMethod subscriberMethod = (SubscriberMethod)var5.next();
                this.subscribe(subscriber, subscriberMethod);
            }
        }
    }

这里的SubscriberMethod封装了订阅方法(使用@Subscribe注解的方法)类型的信息,它的定义如下所示。从下面可以的代码中我们可以看出,实际上该类就是通过几个字段来存储@Subscribe注解中指定的类型信息,以及一个方法的类型变量。

public class SubscriberMethod {
    final Method method;//注册方法
    final ThreadMode threadMode;//线程模式
    final Class<?> eventType;//事件类型
    final int priority;//事件优先级
    final boolean sticky;//是否粘性事件
    String methodString;
    ...
}

register方法通过subscriberMethodFinder实例的findSubscriberMethods方法来获取该观察者类型中的所有订阅方法,然后将所有的订阅方法分别进行订阅。下面我们先看下查找订阅者的方法。

    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap();

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // 这里首先从缓存当中尝试去取该订阅者的订阅方法
        List<SubscriberMethod> subscriberMethods = (List)METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        } else {
         // 当缓存中没有找到该观察者的订阅方法的时候使用下面的两种方法获取方法信息
            if (this.ignoreGeneratedIndex) {
                subscriberMethods = this.findUsingReflection(subscriberClass);
            } else {
                subscriberMethods = this.findUsingInfo(subscriberClass);
            }

            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参数表示是否忽略注解器生成的MyEventBusIndex,该值默认为false。然后,我们会进入到下面的方法中获取订阅方法信息:

   private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
   // 这里通过FindState对象来存储找到的方法信息
        SubscriberMethodFinder.FindState findState = this.prepareFindState();
        findState.initForSubscriber(subscriberClass);
		//从当前类开始,遍历该类的所有父类
        for(; findState.clazz != null; findState.moveToSuperclass()) {
        // 这里通过FindState对象来存储找到的方法信息
            findState.subscriberInfo = this.getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
            // 如果使用了MyEventBusIndex,将会进入到这里并获取订阅方法信息
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                SubscriberMethod[] var4 = array;
                int var5 = array.length;

                for(int var6 = 0; var6 < var5; ++var6) {
                    SubscriberMethod subscriberMethod = var4[var6];
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                // 未使用MyEventBusIndex将会进入这里使用反射获取方法信息
                this.findUsingReflectionInSingleClass(findState);
            }
        }

        return this.getMethodsAndRelease(findState);
    }
   

findUsingReflectionInSingleClass方法从订阅者的所有方法中找到订阅方法

private void findUsingReflectionInSingleClass(FindState findState) {
 Method[] methods;
 try {
     // 获取该类中声明的所有方法
     methods = findState.clazz.getDeclaredMethods();
 } catch (Throwable th) {
     methods = findState.clazz.getMethods();
     findState.skipSuperClasses = true;
 }
 // 对方法进行遍历判断
 for (Method method : methods) {
     int modifiers = method.getModifiers();
     // 这里会对方法的修饰符进行校验
     if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
         // 这里对方法的输入参数进行校验,选择只有1个参数
         Class<?>[] parameterTypes = method.getParameterTypes();
         if (parameterTypes.length == 1) {
             // 获取方法的注解,用来从注解中获取注解的声明信息
             Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
             if (subscribeAnnotation != null) {
                 // 获取该方法的第一个参数
                 Class<?> eventType = parameterTypes[0];
                 if (findState.checkAdd(method, eventType)) {
                     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(...);
         }
     } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
         String methodName = method.getDeclaringClass().getName() + "." + method.getName();
         throw new EventBusException(...);
     }
 }
}

以上是如何获取到订阅类中的所有订阅方法,我们再回到开始的register中,看下订阅的过程:

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        // 将观察者和订阅方法封装成一个Subscription对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // 尝试从缓存中根据事件类型来获取所有的Subscription对象
        CopyOnWriteArrayList<Subscription> subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
        // 指定的事件类型没有对应的观察对象的时候
            subscriptions = new CopyOnWriteArrayList();
            this.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 > ((Subscription)subscriptions.get(i)).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }
    	// 这里又会从“订阅者-事件类型”列表中尝试获取该订阅者对应的所有事件类型
        List<Class<?>> subscribedEvents = (List)this.typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList();
            this.typesBySubscriber.put(subscriber, subscribedEvents);
        }

        ((List)subscribedEvents).add(eventType);
        // 如果是黏性事件还要进行如下的处理
        if (subscriberMethod.sticky) {
            if (this.eventInheritance) {
                Set<Entry<Class<?>, Object>> entries = this.stickyEvents.entrySet();
                Iterator var9 = entries.iterator();

                while(var9.hasNext()) {
                    Entry<Class<?>, Object> entry = (Entry)var9.next();
                    Class<?> candidateEventType = (Class)entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
						// 这里会向该观察者通知所有的黏性事件
                        this.checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = this.stickyEvents.get(eventType);
                this.checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

订阅者(比如MainActivity.this)与事件消费方法(SubscriberMethod)的关系,我们封装到了Subscription中了。而Event和Subscription的关系,我们通过HashMap保存,key为event.class,value即Subscription。

这里涉及到了几个集合,它们是用来做缓存的:

subscriptionsByEventType是根据参数类型存储订阅者和订阅方法,主要是post时使用,因为其存储了订阅者和订阅方法,这两个参数在反射时要用到。

typesBySubscriber根据订阅者存储所有的参数类型,在反注册时可以使用。

有了注册那么肯定还有反注册:

    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = (List)this.typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            Iterator var3 = subscribedTypes.iterator();

            while(var3.hasNext()) {
                Class<?> eventType = (Class)var3.next();
                this.unsubscribeByEventType(subscriber, eventType);
            }

            this.typesBySubscriber.remove(subscriber);
        } else {
            this.logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
    
   private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = (List)this.subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();

            for(int i = 0; i < size; ++i) {
                Subscription subscription = (Subscription)subscriptions.get(i);
                if (subscription.subscriber == subscriber) {//遍历找到该订阅者的订阅事件
                    subscription.active = false;
                    subscriptions.remove(i);//移除
                    --i;
                    --size;
                }
            }
        }

    }


3.发送事件

    private final ThreadLocal<EventBus.PostingThreadState> currentPostingThreadState;

    public void post(Object event) {
        // 这里从线程局部变量中取出当前线程的状态信息
        EventBus.PostingThreadState postingState = (EventBus.PostingThreadState)this.currentPostingThreadState.get();
        // 这里是以上线程局部变量内部维护的一个事件队列
        List<Object> eventQueue = postingState.eventQueue;
        // 将当前要发送的事件加入到队列中
        eventQueue.add(event);
        if (!postingState.isPosting) {
            postingState.isMainThread = this.isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }

            try {
            //循环发送事件
                while(!eventQueue.isEmpty()) {
                    this.postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                // 恢复当前线程的信息
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }

    }

这里的currentPostingThreadState是一个ThreadLocal类型的变量,其中存储了对应于当前线程的PostingThreadState对象,该对象中存储了当前线程对应的事件列表和线程的状态信息等。从上面的代码中可以看出,post方法会不断从当前线程对应的队列中取出事件并进行发布。下面我们看以下这里的postSingleEvent方法。

    private void postSingleEvent(Object event, EventBus.PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (this.eventInheritance) {
            // 这里向上查找该事件的所有父类
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();

            for(int h = 0; h < countTypes; ++h) {
                Class<?> clazz = (Class)eventTypes.get(h);
                // 对上面的事件进行处理
                subscriptionFound |= this.postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = this.postSingleEventForEventType(event, postingState, eventClass);
        }

        if (!subscriptionFound) {
            if (this.logNoSubscriberMessages) {
                this.logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }

            if (this.sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
                this.post(new NoSubscriberEvent(this, event));
            }
        }

    }

在上面的代码中,我们会根据eventInheritance的值决定是否要同时遍历当前事件的所有父类的事件信息并进行分发。如果设置为true就将执行这一操作,并最终使用postSingleEventForEventType对每个事件类型进行处理。

    private boolean postSingleEventForEventType(Object event, EventBus.PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList subscriptions;
        synchronized(this) {
            // 获取指定的事件对应的所有的观察对象
            subscriptions = (CopyOnWriteArrayList)this.subscriptionsByEventType.get(eventClass);
        }

        if (subscriptions != null && !subscriptions.isEmpty()) {
            Iterator var5 = subscriptions.iterator();
        // 遍历观察对象,并最终执行事件的分发操作
            while(var5.hasNext()) {
                Subscription subscription = (Subscription)var5.next();
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;

                try {
                    this.postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }

                if (aborted) {
                    break;
                }
            }

            return true;
        } else {
            return false;
        }
    }

在上面的代码中,我们会通过传入的事件类型到缓存中取寻找它对应的全部的Subscription,然后对得到的Subscription列表进行遍历,并依次调用postToSubscription方法执行事件的发布操作。下面是postToSubscription方法的代码,这里我们会根据订阅方法指定的threadMode信息来执行不同的发布策略。

ThreadMode:

  • POSTING: 默认的模式,开销最小的模式,因为声明为POSTING的订阅者会在发布的同一个线程调用,发布者在主线程那么订阅者也就在主线程,反之亦,避免了线程切换,如果不确定是否有耗时操作,谨慎使用,因为可能是在主线程发布
  • MAIN:主线程调用,视发布线程不同处理不同,如果发布者在主线程那么直接调用(非阻塞式),如果发布者不在主线程那么阻塞式调用
  • MAIN_ORDERED:和MAIN差不多,主线程调用,和MAIN不同的是他保证了post是非阻塞式的(默认走MAIN的非主线程的逻辑,所以可以做到非阻塞)
  • BACKGROUND:在子线程调用,如果发布在子线程那么直接在发布线程调用,如果发布在主线程那么将开启一个子线程来调用,这个子线程是阻塞式的,按顺序交付所有事件,所以也不适合做耗时任务,因为多个事件共用这一个后台线程
  • ASYNC:在子线程调用,总是开启一个新的线程来调用,适用于做耗时任务,比如数据库操作,网络请求等,不适合做计算任务,会导致开启大量线程
    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch(subscription.subscriberMethod.threadMode) {
        case POSTING:
            this.invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                this.invokeSubscriber(subscription, event);
            } else {
                this.mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (this.mainThreadPoster != null) {
                this.mainThreadPoster.enqueue(subscription, event);
            } else {
                this.invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                this.backgroundPoster.enqueue(subscription, event);
            } else {
                this.invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            this.asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }

    }

这里的invokeSubscriber会在当前线程中立即调用反射来触发指定的观察者的订阅方法。否则会根据具体的情况将事件加入到不同的队列中进行处理。

这里的mainThreadPoster最终继承自Handler,当调用它的enqueue方法的时候,它会发送一个事件并在它自身的handleMessage方法中从队列中取值并进行处理,从而达到在主线程中分发事件的目的。

这里的backgroundPoster实现了Runnable接口,它会在调用enqueue方法的时候,拿到EventBus的ExecutorService实例,并使用它来执行自己。在它的run方法中会从队列中不断取值来进行执行。

问题

1、 在EventBus中,使用@Subscribe注解的时候指定的ThreadMode是如何实现在不同线程间传递数据的?

要求主线程中的事件通过Handler来实现在主线程中执行,非主线程的方法会使用EventBus内部的ExecutorService来执行。实际在触发方法的时候会根据当前线程的状态和订阅方法的ThreadMode指定的线程状态来决定何时触发方法。非主线程的逻辑会在post的时候加入到一个队列中被随后执行。

2、黏性事件是否是通过内部维护了之前发布的数据来实现的,是否使用了缓存?

黏性事件会通过EventBus内部维护的一个事件类型-黏性事件的哈希表存储,当注册一个观察者的时候,如果发现了它内部有黏性事件监听,会执行post类似的逻辑将事件立即发送给该观察者。


总结

1. 注册事件

在这里插入图片描述

涉及参数

Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // Subscription类保存了要注册的类对象以及当前的subscriberMethod
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; //保存了以eventType为key,Subscription对象集合为value的键值对,根据参数类存储订阅者和订阅方法
Map<Object, List<Class<?>>> typesBySubscriber; //保存了以当前要注册类的对象为key,注册类中订阅事件的方法的参数类型的集合为value的键值对,根据订阅者存储所有参数类型

总结:主要是register方法,获取注册类中订阅了事件的所有方法(即使用了Subscribe注解、有public修饰符、一个参数的方法),然后看缓存中(subscriptionsByEventType和typesBySubscriber)有没有,没有就保存进subscriptionsByEventType和typesBySubscriber(两个 HashMap,subscriptionsByEventType在发送事件时使用,typesBySubscriber在取消注册时使用)

2.发送事件(含线程切换)

在这里插入图片描述

总结:调用post方法,获取事件的类型,根据类型从subscriptionsByEventType中获取订阅者和订阅方法,然后根据 发送事件的线程 和 事件订阅的线程模式 判断是否需要切换线程(子线程往主线程切换用handler、主线程往子线程切换用线程池),然后通过反射调用订阅者的订阅方法,完成通讯

3.粘性事件

总结

  1. 调用postSticky方法,postSticky()方法主要做了两件事:先将事件类型和对应事件保存到stickyEvents中,方便后续使用;然后执行post(event)继续发送事件,这个post()方法就是之前发送的post()方法(所以,如果在发送粘性事件前,已经有了对应类型事件的订阅者,及时它是非粘性的,依然可以接收到发送出的粘性事件)。
  2. 在注册订阅事件的时候,遍历stickyEvents,如果当前要注册的事件订阅方法是粘性的,并且该方法接收的事件类型和stickyEvents中某个事件类型相同或者是其父类,则响应订阅事件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章