天天看点

Android事件总线3 EventBus3.2原理一 概述二 源码解析EventBus三 总结

一 概述

上两篇文章对 EventBus 的使用做了详细的分析。了解到 EventBus 是发布/订阅者模式,适用于 Android 和 Java 的发布/订阅事件总线。主要功能是替代 Intent、Handler、BroadCast 在 Activity、Fragment、Service 线程之间传递消息。它能够简化应用组件间的通信,解耦 (有效分离) 事件的发送者和接收者,避免复杂和容易出错的依赖和生命周期问题,开销小,代码更优雅。

Android事件总线3 EventBus3.2原理一 概述二 源码解析EventBus三 总结

EventBus 的官网原理图我们再看一遍,发布者通过 EventBus 发布事件,订阅者通过 EventBus 订阅事件,当发布者发送事件时,订阅该事件的订阅者的事件处理方法将被调用。从图中看出,发布者发送一个事件时,则该事件将会同时传递给一个或多个该事件的订阅者。

二 源码解析EventBus

2.1 注册事件

EventBus 的使用这里就不讲解了,我们主要讲解一下 EventBus 的源码的实现。注册事件的方式如下:

通过 EventBus.getDefault() 获取 EventBus 实例,getDefault() 只是一个双重锁检查单例模式,保证全局只有一个实例:

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;
    }
           

下面看看 EventBus 的构造方法里面做了什么事情:

public EventBus() {
        this(DEFAULT_BUILDER);
   }
           

this() 表示 EventBus 的一个构造方法,DEFAULT_BUILDER 表示默认构建的 EventBusBuilder,用于构造 EventBus。

EventBus 的构造方法中,通过 EventBusBuilder 来进行配置:

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;
   }
           

通过建造者模式 EventBusBuilder 对 EventBus 的相关信息配置,当然我们也可以通过 EventBusBuilder 更改 EventBus 的配置:

EventBus.builder()
            .sendSubscriberExceptionEvent(false)
            .sendNoSubscriberEvent(false)
            .build()
            .register(this);
           

通过 build() 构建一个当前配置的 EventBus 实例,register(this) 也可以注册事件。

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //找出当前订阅者的所有订阅方法
        List<SubscriberMethod> subscriberMethods =
            subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {//同步
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);//对订阅者方法注册
            }
        }
    }
           

注册事件里面只做了两件事:

  • 找出当前订阅者的所有订阅方法
  • 对订阅者方法进行注册

查找订阅者方法返回一个 SubscriberMethod 对象的集合,SubscriberMethod 是方法的相关属性对象。

public class SubscriberMethod {
    final Method method;//方法对象
    final ThreadMode threadMode;//执行线程
    final Class<?> eventType;//接收事件的类型
    final int priority;//事件优先级
    final boolean sticky;//是否为粘性事件
    /** Used for efficient comparison */
    String methodString;
}
           

从 findSubscriberMethods() 开始查找方法:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    	//METHOD_CACHE是一个ConcurrentHashMap,根据subscriberClass保存了SubscriberMethod,
    	//提高保存效率,防止重复查找从缓存中查找是否有订阅的方法集合,如果有则直接返回
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
		//ignoreGeneratedIndex默认为false,如果使用默认的EventBusBuilder,则忽略注解生成器
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }     
        METHOD_CACHE.put(subscriberClass, subscriberMethods);//缓存查找到的订阅事件方法
        return subscriberMethods; 
    }

           

findSubscriberMethods() 首先去缓存中查找,如果找到则直接返回;如果找不到则去下一级查找,找到后缓存起来,如果我们使用默认方式获取 EventBus 对象,即 ignoreGeneratedIndex = false,那么来到 findUsingInfo():

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    	//FindState是包含订阅者方法和订阅者信息的对象,包含了所有订阅者的方法、事件类型、保存方法的key等信息
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        //循环判断
        while (findState.clazz != null) {//初始状态下的findState.clazz就是subscriberClass
            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();//修改findState.clazz为subscriberClass的父类class
        }
        //将findState中的SubscriberMethod集合放到一个新的List中并且返回,释放掉findState
        return getMethodsAndRelease(findState);
    }
           

findUsingInfo() 会在当前要注册的类以及父类中查找订阅者方法,FindState 类用来辅助查找订阅事件方法,具体过程在 findUsingReflectionInSingleClass() 方法中,它通过反射查找订阅事件方法:

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
    	······
        //遍历当前类方法,查找到符合条件的
        for (Method method : methods) {
            int modifiers = method.getModifiers();//获得方法修饰符
            //如果是public,但非abstract、static等
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            	//获得当前方法所有参数类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                //如果只有一个参数
                if (parameterTypes.length == 1) {
                	//如果方法使用了Subscribe注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                    	//得到该参数类型
                        Class<?> eventType = parameterTypes[0];
                        //判断findState的anyMethodByEventType是否添加过eventType的方法,没有则返回true
                        if (findState.checkAdd(method, eventType)) {
                        	//获取线程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //通过实践类型、线程模式、优先级、是否为粘性事件创建一个SubscriberMethod
                            //添加到subscriberMethods方法集合中
                            findState.subscriberMethods.add(new SubscriberMethod(
                                method, eventType, threadMode, subscribeAnnotation.priority(), 
                                subscribeAnnotation.sticky()));
                        }
                    }
                }
            } 
        }
    }
           

到这里 register() 的 findSubscriberMethods() 查找订阅者事件方法分析完了,就是找到当前注册类以及父类中订阅事件的方法的集合,接着来看看 register() 的 subscribe() 方法:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
  		//获取当前事件方法的事件类型
        Class<?> eventType = subscriberMethod.eventType;
        //Subscription中保存了要注册的类对象以及当前的subscriberMethod方法体
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //subscriptionsByEventType是一个以eventType为key,Subscription为value的键值对HasMap
        //subscriptionsByEventType是否存在该事件类型的值
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //如果不存在subscriptions则创建一个subscriptions,并保存到subscriptionsByEventType中
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } 

        int size = subscriptions.size();
        //将上面的newSubscription添加到subscriptions中保存
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority >
                    subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }
		//typesBySubscriber是一个注册类对象为key,该注册类的事件类型集合为value的HasMap
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        //如果不存在subscribedEvents则创建一个,并保存到typesBySubscriber中
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //保存当前订阅事件的事件类型
        subscribedEvents.add(eventType);
		//粘性事件--后面另外分析
        if (subscriberMethod.sticky) {
        ······
        }
    }
           

这是注册的核心,所以 subscribe() 主要是获得以事件类型为 key,Subscription 为 value 的 subscriptionsByEventType,获得以注册类对象为 key,该注册类的事件类型集合为 value 的 typesBySubscriber,在发送事件的时候使用 subscriptionsByEventType 完成事件的处理,在注销 EventBus 的时候对 subscriptionsByEventType 和 typesBySubscriber 相关资源释放。

注册订阅者流程:

  • 通过 registere() 注册一个订阅者
  • 获取当前订阅者所有订阅方法(方法缓存 METHOD_CACHE 如果有则直接返回,没有则通过反射和 @Subscribe 注解找到方法名集合并保存到 METHOD_CACHE 中)
  • 根据订阅者的订阅事件类型,将订阅者存储到以订阅者事件类型为 key,所有订阅者信息为 value 的 HasMap 集合中
  • 然后将订阅事件保存到以订阅者为 key,订阅者所有订阅事件类型为 value 的 HasMap 集合中

2.2 反注册事件

EventBus 通过下面的方法注销事件:

进入 unregister() 看看:

public synchronized void unregister(Object subscriber) {
  		//根据当前注册类对象获得对应事件类型集合
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
        	//遍历,释放当前subscriber的Subscription
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            //移除typesBySubscriber中的某个注册类对象的事件类型集合
            typesBySubscriber.remove(subscriber);
        }
    }
           

注销给定事件类型的订阅者,就是释放不需要用到的订阅者数据,来到 unsubscribeByEventType():

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
   		//得到事件类型的Subscription集合
        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);
                //如果subscription的订阅者对象类和当前要取消注册的注册类对象一致,则移除当前Subscription
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }
           

所以在 unregister() 方法中就是释放事件方法集合和事件类型集合的资源。

注销订阅者的流程:

  • 通过 unregister() 得到要注销的订阅者
  • 获取该订阅者的所有订阅事件类型集合
  • 遍历事件类型集合,根据事件类型获取所有订阅者集合,从集合中删除该订阅者
  • 移除该注册者对应的事件类型集合的事件类型

2.3 发送事件

EventBus 通过下面的代码发送普通事件:

发送事件就是通过 post() 方法完成的:

public void post(Object event) {
  		//currentPostingThreadState是一个PostingThreadState类型的本地线程
  		//ThreadLocal<PostingThreadState> PostingThreadState 保存了线程队列、线程模式、事件方法等信息
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        //将当前事件添加到本地线程的事件队列中
        eventQueue.add(event);
		// isPosting 默认为 false
        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();//是否为主线程
            postingState.isPosting = true;
            try {
            	//遍历事件队列
                while (!eventQueue.isEmpty()) {
                	//发送单个事件,并且从事件队列中移除这个事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }
           

所以 post() 方法是先将发送的事件保存到本地线程的事件队列中,然后循环出队列,将事件交给 postSingleEvent() 处理:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;//是否找到subscription(subscription包含事件方法的信息)
        if (eventInheritance) {//是否有继承事件,默认为true,表示是否向上查找事件的父类
        	//根据当前事件,查找所有事件中当前事件类型的Class集合
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            //遍历Class集合,继续处理事件
            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 (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
           

这根据 eventInheritance 事件继承性来是否向上根据事件 Class 来查找所有该 Class 类型的事件,然后 postSingleEventForEventType() 进一步处理,如果找不到该事件的处理方法则发送找不到事件的处理:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState,
           Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
        	//获取该事件类型对应的subscription集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        //如果该事件类型有对应的订阅事件
        if (subscriptions != null && !subscriptions.isEmpty()) {
        	//遍历事件方法体集合
            for (Subscription subscription : subscriptions) {
                postingState.event = event;//记录事件
                postingState.subscription = subscription;//记录subscription
                boolean aborted;
                try {
                	//最终事件处理
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                	//最后将相关状态设置为初始状态
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
            }
            return true;
        }
        return false;
    }
           

遍历发送该事件类型的所有 subscription (subscription 记录订阅事件的相关信息),也就说明发布者发布事件,能发送给对一个或多个该事件的订阅者。最后来到了 postToSubscription() 最终事件处理方法。

发送事件流程:

  • 获取当前线程事件队列,将要发送的事件添加到队列中
  • 根据订阅者 eventClass 向上查找所有的事件类型集合 eventTypes
  • 分别获取事件类型集合中的订阅者信息集合 subscriptions,再在订阅者信息集合分别根据事件类型 eventClass 获取对应的订阅者信息 subscription,最后去处理事件

2.4 处理事件

处理事件是根据订阅事件的方法的线程模式,直接或间接通过反射的原理来执行订阅者的事件处理方法:

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 {
                	//如果发送事件的线程是子线程,则将事件加入主线程队列,通过Handler切换到主线程处理事件
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            //无论在哪一个线程发布事件,都先将事件加入主线程队列,通过Handler切换到主线程依次处理事件
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    //注意:订阅者与发布者没有关联在技术上是不正确的
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
            	//如果发布事件的线程是主线程,则先将事件加入后台线程队列,然后通过线程池依次处理事件
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                //如果发布事件的线程是子线程,则直接通过反射处理事件
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
            	//无论发布事件是在哪一个线程,都将事件加入后台线程队列,通过线程池依次处理事件
                asyncPoster.enqueue(subscription, event);
                break;
        }
    }
           

可以看到 postToSubscription() 订阅者处理事件的线程模式以及发送事件的线程来判断如何处理事件,主要有两种:

  • 直接调用 invokeSubscriber(),通过反射处理订阅事件方法
  • 将事件加入消息队列,依次执行处理订阅事件的方法

1.在相应线程直接通过 invokeSubscriber() 方法,用反射的原理来执行订阅事件的处理方法:

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);
        }
    }
           

这样发出去的事件就被订阅者的事件处理函数接收到并做相应处理。

2.先将事件和订阅者封装成 PendingPost 加入 enqueue() 消息队列,队列在排队等待执行,这里对 mainThreadPoster.enqueue(subscription, event) 做进一步的分析,其他线程模式的 enqueue() 原理都是一样的:

interface Poster {//事件发送
	//为一个订阅发布事件排队执行
    void enqueue(Subscription subscription, Object event);
}
           

Poster 是一个接口,实现了 enqueue() 方法,我们来看看 mainThreadPoster 的内部构造:

mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;

public interface MainThreadSupport {
    Poster createPoster(EventBus eventBus);
    class AndroidHandlerMainThreadSupport implements MainThreadSupport {
        private final Looper looper;
        public AndroidHandlerMainThreadSupport(Looper looper) {
            this.looper = looper;//主线程looper
        }
        @Override
        public boolean isMainThread() {
            return looper == Looper.myLooper();//是否为主线程
        }
        @Override
        public Poster createPoster(EventBus eventBus) {
            return new HandlerPoster(eventBus, looper, 10);//返回Poster实例
        }
    }
}
           

mainThreadPoster 由 mainThreadSupport.createPoster() 创建,所以 mainThreadPoster 是 HandlerPoster 的一个实现类,来看看HandlerPoster 的内部实现:

public class HandlerPoster extends Handler implements Poster {
	······
    protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);//主线程looper
        ······
    }

    public void enqueue(Subscription subscription, Object event) {
    	//用subscription和event封装成一个PendingPost对象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
        	//加入消息队列
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                //发送处理事件的消息,handleMessage()被执行,完成从其他线程切换到主线程中
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {//循环遍历队列
            	//移除队列
                PendingPost pendingPost = queue.poll();               
                //eventBus处理事件方法
                eventBus.invokeSubscriber(pendingPost);
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}
           

HandlerPoster 中 enqueue() 将 PendingPost 对象 (包含事件方法等信息) 保存到队列中,在 handleMessage() 中循环遍历将消息逐个移除并交给 eventBusinvokeSubscriber() 处理事件:

void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        //释放PendingPost引用的资源
        PendingPost.releasePendingPost(pendingPost);
        if (subscription.active) {//订阅事件存在
        	//反射来执行订阅事件处理方法
            invokeSubscriber(subscription, event);
        }
    }
           

这里主要就是将 PendingPost 资源释放,并且如果事件存活,则通过反射来处理订阅事件方法。

  • mainThreadPoster.enqueue(subscription, event): 核心就是将事件加入线程队列中,然后通过 Handler 切换线程处理事件
  • backgroundPoster.enqueue(subscription, event): 原理同上,先将事件加入队列,然后再移除队列处理事件,但是会通过线程池做进一步的处理
  • asyncPoster.enqueue(subscription, event): 原理同上

2.5 粘性事件

一般情况,我们使用 EventBus 都是先注册事件,实现事件方法处理函数,再发送事件,即先注册,后发布;但是粘性事件恰恰相反,我们可以先发送事件,然后再注册、处理事件函数;即先发布,后注册。

我们先从发布粘性事件开始:

来看看发送粘性事件的内部方法 postSticky():

public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        post(event);//发送事件
    }
           

这里就做了两件事,stickyEvents 保存事件类型以及对应的粘性事件,然后通过 post() 发送事件,这里的发送事件与发送普通事件一致。所以如果在发送粘性事件之前,如果有相同类型的事件订阅者,并且是非粘性的,那么订阅者依然可以接受到发送出来的粘性事件。

发送完粘性事件之后,再注册事件、实现事件处理方法。核心的注册流程还是上面的 register() 中的 subscribe() 方法,有一段粘性事件的代码没有分析:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
  		······
		//如果订阅者事件处理方法sticky = true,表示该方法接收粘性事件
        if (subscriberMethod.sticky) {
        	//默认为true,表示是否向上查找该事件的所有子类
        	//需要考虑该事件类型的所有子类现有的粘性事件
            if (eventInheritance) {
                //stickyEvents发送粘性事件时,保存事件类型和对应事件
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    //当前事件类型是否与candidateEventType一致或者是其子类
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();//获得对应事件
                        //处理粘性事件
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }
           

粘性事件的处理就是在注册 EventBus 的时候,遍历粘性事件 stickyEvents 集合,如果当前要注册的订阅事件方法是粘性的,并且该方法接收事件类型与粘性事件 stickyEvents 集合中的某个事件类型相同或者同类,则取出 stickyEvents 对应事件类型的事件,通过 checkPostStickyEventToSubscription() 做进一步处理:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
        	//事件处理
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }
           

最终通过 postToSubscription() 方法对粘性事件完成处理。这就是粘性事件的整个处理过程。

粘性事件的流程:

  • 发送粘性事件的时候先根据事件类型保存粘性事件,然后再和普通事件一样post()发送事件;
  • 在注册事件时根据事件处理方法判断 sticky = true 是粘性事件,如果 stickyEvents 中 eventType 一致则处理粘性事件,直接 postToSubscription() 响应处理事件。

三 总结

3.1 注册事件原理

通过 registere() 注册一个订阅者,先获取当前订阅者所有的订阅方法 subscriberMethods,如果方法缓存 METHOD_CACHE 中存在则直接返回,没有则通过反射和 @Subscribe 注解找到方法名集合并保存到 METHOD_CACHE 中;然后根据订阅者的 eventType 将所有订阅者信息 subscriptions 缓存到 subscriptionsByEventType(HashMap) 中,根据订阅者将订阅者的所有事件类型 subscribedEvents (eventType 集合)缓存到 typesBySubscriber(HashMap) 中。

3.2 注销事件原理

通过 unregister() 得到要注销的订阅者,从 typesBySubscriber 获取该订阅者的所有订阅事件类型集合 subscribedTypes;从该集合中根据事件类型 eventType 获取所有订阅者信息集合 subscriptions,从集合中删除 remove() 该订阅者信息;再将 typesBySubscriber 对应的订阅者的事件类型集合移除 remove()。

3.3 发送并处理事件原理

获取当前线程事件队列 ThreadLocal,将要发送的事件 event 添加到队列中;然后根据 eventClass 向上查找所有的事件类型集合 eventTypes,分别获取所有的事件类型集合中的订阅者信息集合 subscriptions,再在订阅者信息集合根据事件类型 eventClass 获取对应的订阅者信息 subscription,最后去处理事件。

处理事件 postToSubscription() 是根据订阅事件的方法的线程模式,如果符合线程模式则直接执行 invokeSubscriber() 方法,用反射的原理 invoke() 来执行订阅事件的处理方法;如果不符合线程模式,则通过 Poster 类先将事件 event 和订阅者信息 subscription 封装成 PendingPost 加入 enqueue() 消息队列,队列在排队等待执行(后台线程会通过线程池 pool 做进一步处理),切换对应的线程后 invokeSubscriber() 执行订阅事件的处理函数。

3.4 粘性事件原理

发送粘性事件的时候先根据事件类型保存粘性事件 stickyEvents;然后再和普通事件一样 post() 发送事件;因为粘性事件是先发布,后注册响应事件的,所以在注册事件时 sticky = true 则进行粘性事件处理,如果 stickyEvents 中 eventType 一致则处理粘性事件,直接 postToSubscription() 响应处理事件。

3.5 EventBus优缺点

优点

  • EventBus 是一个 Android 平台轻量级的事件总线框架,它简化了 Activity、Fragment、Service 等组件之间的交互,很大程度上降低了它们之间的耦合,使得代码更加简洁,耦合性更低,提升代码质量
  • EventBus 使用注解来标识接收函数,这样函数名不受限制

缺点

  • 因为 EventBus 用到了反射,如果大量的使用的情况下,对应用的性能多少有点影响

继续阅读