EventBus源码分析

前言:EventBus在现如今Android项目中用来进行通信使用很广泛。在本篇文章中,我将会分为下面几个模块来讲解EventBus:如何使用,源码分析,使用注意事项,高级技能。

  • 在官网中有这么一句话来描述EventBus
    Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality,意思就是说EventBus用来在Android和Java中简化Activities, Fragments,线程和服务之间的通信,具有代码量少,质量高的优点,另外包的体积很小,只有50KB大小,并且响应速度快。

1 如何使用:

  • 首先声明一个Event class:

     public class MessageEvent{}
    
  • 声明并且注解订阅方法,可以通过注解选择指定的线程:

    @Subscribe(threadMode = ThreadMode.MAIN)  
    public void onMessageEvent(MessageEvent event) {/* Do something */};
    
  • 注册和解除注册订阅者,对于Android的Activity, Fragment,一般应该依据生命周期来进行注册和解除注册:

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }
    
    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }
    
    
    
  • 发送Event事件

    EventBus.getDefault().post(new MessageEvent());
    
  • 发送的时候就会执行上面Do something中的内容。可以把EventBus看作一个观察者模式来理解,不太了解观察者模式的可以点击 Java设计模式-观察者模式 查看。EventBus作为一个被观察者,通过register持有观察者对象,而通过post方法就将事件传递到观察者中执行了。

    讲解完基本使用方法后我们主要来看一下源码部分,学习源码要避免深入细节不可自拔,只见树木不见森林,学习框架源码有很多好处,其中最主要的是有助于我们更好的使用框架,另外在看框架源码的过程中可以学习优秀框架的思想,EventBus源码其实也比较简单,下面来看一下。

2 源码部分

  • 我们首先是要获取EventBus实例对象,通过EventBus.getDefault(),那么首先看看getDefault方法,这是一个典型的双重锁定单例模式,不太了解观察者模式的可以点击设计模式——单例模式(创建型模式)查看,volatile关键字具有禁止指令重排序的功能,我们还看到了有一个类EventBusBuilder,而且而且EventBus中builder()方法是public修饰的,那么我们可以通过EventBus.builder().build()来定制自己的EventBus实例,使用的是建造者模式,建造者模式可以点击建造者模式了解,区别是一个是加锁的,一个没加锁的,不过基本不这么用。

    public class EventBus {
    	static volatile EventBus defaultInstance;
    		
        /** Convenience singleton for apps using a process-wide EventBus instance. */
        public static EventBus getDefault() {
            if (defaultInstance == null) {
                synchronized (EventBus.class) {
                    if (defaultInstance == null) {
                        defaultInstance = new EventBus();
                    }
                }
            }
            return defaultInstance;
        }
    	public EventBus() {
            this(DEFAULT_BUILDER);
        }
    
        EventBus(EventBusBuilder builder) {
            logger = builder.getLogger();
            subscriptionsByEventType = new HashMap<>();
            typesBySubscriber = new HashMap<>();
            stickyEvents = new ConcurrentHashMap<>();
            mainThreadSupport = builder.getMainThreadSupport();
            mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
            backgroundPoster = new BackgroundPoster(this);
            asyncPoster = new AsyncPoster(this);
            indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
            subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                    builder.strictMethodVerification, builder.ignoreGeneratedIndex);
            logSubscriberExceptions = builder.logSubscriberExceptions;
            logNoSubscriberMessages = builder.logNoSubscriberMessages;
            sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
            sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
            throwSubscriberException = builder.throwSubscriberException;
            eventInheritance = builder.eventInheritance;
            executorService = builder.executorService;
        }
        
    	public static EventBusBuilder builder() {
        	return new EventBusBuilder();
    	}
    
  • 获取了实例之后就要执行注册register()方法了,我们来看一下这个方法:

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
    	// 1 
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
    // 2
    public class SubscriberMethod {
        final Method method;
        final ThreadMode threadMode;
        final Class<?> eventType;
        final int priority;
        final boolean sticky;
        /** Used for efficient comparison */
        String methodString;
    
    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }
    // 3
    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
    
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) { // 4
            return subscriberMethods;
        }
    	// 5 
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = 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;
        }
    }
    
    
    
  • 首先获取注册对象的Class类型,然后在标注1处通过subscriberMethodFinder实例查找注册对象的订阅方法,订阅方法类在标注2处,其中持有method, threadMode , eventType, priority, sticky,其实subscriberMethodFinder是通过一个Map对象来操作的, 请看标注3处。 如果执行到标注4处,缓存中没有的话就会接着往下走,标注5处默认为false,所以我们直接看findUsingInfo方法,方法如下:

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
            FindState findState = prepareFindState();
            findState.initForSubscriber(subscriberClass);
            while (findState.clazz != null) {
                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 {
                    findUsingReflectionInSingleClass(findState);
                }
                findState.moveToSuperclass();
            }
            return getMethodsAndRelease(findState);
        }
        
    
  • 这里使用了FindState,首先通过prepareFindState来返回一个findState实例,然后通过initForSubscriber将注册类绑定到实例中,便于后期处理。这里有一个while循环,我们因为一般情况下走else逻辑,所以我们直接分析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
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        // 遍历方法
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            // 方法的权限修饰符必须是public的,并且非static,非abstract的
            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];
                        // 将符合条件的方法封装成SubscriberMethod方法放入findState的subscriberMethods列表中
                        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("@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");
            }
        }
    }
    
  • 这里很明显是通过反射来使用的,首先通过方法getDeclaredMethods获取注册类中的方法,其中包括公共 保护 默认 私有的方法 但是不包括继承的方法以及接口中default的方法, 如果抛出异常NoClassDefFoundError的话就会使用getMethods方法来获取注册类中的方法,其中包括public声明的方法包括 public声明的继承的方法,以及接口中的default的方法。获取到方法列表之后,就要对方法进行遍历处理了,注解中写的比较清晰,就不详细说了,需要注意的是,这里有几个异常需要注意:

    1 如果你的订阅方法的修饰符不符合要求,会抛出EventBusException异常;
    2 如果你的参数列表中个数不是一个的话,也会抛出EventBusException异常。
    3 如果你的注册类中没有@Subscriber修饰的方法的话,也会抛出EventBusException异常(这个异常是在方法findSubscriberMethods中抛出的)

  • 然后我们回到register中看subscriber方法:

    	public void register(Object subscriber) {
            Class<?> subscriberClass = subscriber.getClass();
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
    	}
    
    	// Must be called in synchronized block
        private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    	private final Map<Object, List<Class<?>>> typesBySubscriber;
    	
    	private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            Class<?> eventType = subscriberMethod.eventType;
            // 将观察者和订阅方法封装成一个对象,然后放入subscriptionsByEventType中
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            // 从缓存中尝试获取事件类型对应的subscriptions列表
            CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions == null) {
                subscriptions = new CopyOnWriteArrayList<>();
                subscriptionsByEventType.put(eventType, subscriptions);
            } else {
            	//如果subscriptions中已经包含了newSubscription对象,就会抛出异常,这里equals方法被重写了
                if (subscriptions.contains(newSubscription)) {
                    throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                            + eventType);
                }
            }
    
            int size = subscriptions.size();
            // 这里会根据优先级顺序决定newSubscription在队列中的位置
            for (int i = 0; i <= size; i++) {
                if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                    subscriptions.add(i, newSubscription);
                    break;
                }
            }
    
    		// 尝试根据注册者,获取对应的事件类型的列表
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
            subscribedEvents.add(eventType);
    
    		// 如果黏性事件的话,按照如下流程处理
            if (subscriberMethod.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);
                }
            }
    	}
    
  • 上述subscribe方法的主线也比较清晰,主要是使用了subscriptionsByEventTypetypesBySubscriber来进行存储相关的信息。

  • unregister方法就是将所有的订阅信息从缓存中清除掉,如下:

    	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 {
                logger.log(Level.WARNING, "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--;
                    }
                }
            }
        }
    
  • 接下来我们看最后的post方法,

        public void post(Object event) {
            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;
                }
            }
        }
    
        final static class PostingThreadState {
            final List<Object> eventQueue = new ArrayList<>();
            boolean isPosting;
            boolean isMainThread;
            Subscription subscription;
            Object event;
            boolean canceled;
        }
    
  • 首先currentPostingThreadState是一个ThreadLocal对象,这里ThreadLocal能保证每一个线程都单独有一个currentPostingThreadState对象,至于是怎么做到的,网上有很多讲解的文章,就不详细说了,简单来说就是因为每一个Thread都会持有ThreadLocal.ThreadLocalMap对象,很明显这是一个类似于Map的对象,可以按照一个Map来理解,而这个map的key就是ThreadLocal本身,所以就会导致每一个线程都会持有自己独一份的对象,互不干涉其它线程。最终走到while循环,不停的循环发送事件,直到队列中事件处理完毕。然后走进postSingleEvent方法:

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        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 {
            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));
            }
        }
    }
    
  • eventInheritance通过lookupAllEventTypesaddInterfaces可以看出,代表的是是否遍历当前事件的所有父类事件信息,默认是true,我们进入postSingleEventForEventType方法:

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                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;
    }
    
  • 这里有我们熟悉的对象subscriptionsByEventType,它存储的就是我们事件类型对应的Subscription集合,其中包含的就是订阅方法信息和观察者,然后for循环遍历集合,执行postToSubscription方法:

    	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);
            }
        }
    
  • 上面的方法逻辑就很明白了,就是根据threadMode来决定执行的逻辑,最终都执行到了invokeSubscriber方法中, 其中mainThreadPoster查看源码直到是一个Handler,在handleMessage中执行到了方法invokeSubscriber(pendingPost)方法(这是一个方法重载),最终也是执行到了invokeSubscriber(Subscription subscription, Object event)

    void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        PendingPost.releasePendingPost(pendingPost);
        if (subscription.active) {
            invokeSubscriber(subscription, event);
        }
    }
    
    void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }
    
  • 我们能看到最终是使用反射调取到了方法,执行了我们真实的订阅方法的逻辑,至此源码分析结束。

3 注意事项

除了上面说的几个Exception,如果我们重复调用register的话,会出现throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);异常,但是如果重复unregister倒是不影响,会有log提示Subscriber to unregister was not registered before xxx.

4 高级技能

  • 我们可以查看到有一个API是hasSubscriberForEvent,这个API可以用来判断是否注册了某一个event事件, 这个的用处是很大的,比如应用升级,如果应用在前台的话,升级就会影响用户体验,这时候就可以使用这个,我们在用户将应用退出前台的时候post升级event事件在升级类中后台执行升级逻辑,不影响用户体验。这个和在Activity中生命周期中处理是不同的,因为我们不可能将升级的逻辑放在Activity中处理,这时候我们有一个单独的类处理升级逻辑,就可以在Activity生命周期onStop中post事件,在升级类中处理,这个需要仔细的琢磨一下。
  • 我们可以查看到有一个API是isRegistered, 这个是判断是否注册了某一个观察者,这个观察者不仅仅是Activity, 也可以是Java类等,注册之前判断一下,避免重复注册出现crash.

学习源码有助于我们更好的使用这个工具,并且从中学到很多好的思想,但是源码的学习首先要将脉络理清楚,避免深入细节不可自拔,文中有些细节读者可以自行深入研究,相信你会有很不错的收获的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值