三方框架必学系列#EventBus

51 阅读17分钟

三方框架必学系列#EventBus

三方框架必学系列#Rxjava

三方框架必学系列#Retrofit

三方框架必学系列#OkHttp

三方框架必学系列#Glide

三方框架必学系列#屏幕适配

一.EventBus是什么?

EventBus是消息事件总线,基于订阅发布者模型,简化了Activity、Fragment、Threads、Services的各个组件间、组件内和线程间的通讯。官网地址:github.com/greenrobot/…,它的优点很明显:1.简化了组件间通讯方式;2.通信双方可以做到解耦;3.使用ThreadMode灵活切换线程;当然它也有缺点,比如通过注解反射方式收集和执行回调方法,如果忘了反注册,可能导致能存泄漏,但这都不妨碍它是一个优秀的、体积小、效率高的简单的组件通讯框架。

二.基本使用

1.基本依赖

implementation 'org.greenrobot:eventbus:3.1.1'

2.事件的订阅和发布

1.注册

通过 @Subscribe注解可以设置接收事件的线程、优先级、以及是否是粘性事件。

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    EventBus.getDefault().register(this);

}
   //接收普通事件
    @Subscribe
    public void subscribeEvent(String event) {
        System.out.println("subscribeEvent---->>:" + event);
    }

    //设置接收等级
    @Subscribe(priority = 1)
    public void subscribeEventPriority(String event) {
        System.out.println("subscribeEventPriority---->>:" + event);
    }

2.普通事件发布

public void post(View view) {
EventBus.getDefault().post("post simo");
}

    /**
     * 接收粘性事件
     * @param event
     */

    @Subscribe(sticky = true)
    public void subscribeEventSticky(String event) {
        System.out.println("subscribeEventSticky---->>:" + event);
    }

3.粘性事件发布

    public void postStick(View view) {
        //查看源码得知 postSticky会导致post同类型的方法也会执行,我们可以设置自定义Event 通过eventType来标记
//        synchronized (stickyEvents) {
//            stickyEvents.put(event.getClass(), event);
//        }
//        // Should be posted after it is putted, in case the subscriber wants to remove immediately
//        post(event);
        EventBus.getDefault().postSticky("post simo sticky->>>>>>");
        startActivity(new Intent(this, SecondActivity.class));
    }

3.通过APT高效执行

EventBus3.x的版本可以设置APT模式,不完全使用注解来收集订阅方法来实现,我们如果想要使用APT,需要先做一些配置:

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                // 根据项目实际情况,指定辅助索引类的名称和包名
                arguments = [ eventBusIndex : 'com.seven.eventbus.MyEventBusIndex' ]
            }
        }
    }
}
dependencies {
    compile 'org.greenrobot:eventbus:3.1.1'
    // 引入注解处理器
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}

然后在项目的 Application 中添加如下配置,以生成一个默认的 EventBus 单例

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

4.APT执行流程

后面会仿照APT+JAVAPOET写一个版本,这里我简单的理下它的基本原理:它是通过apt在编译时就会对有注解的方法进行解析,当我们在build.gradle中指定包名和类名,注解处理器就会帮我们生成一个MyEventBusIndex类,而这个类呢,会在静态方法中将我们注册对象和订阅方法做map映射,然后我们再在application中将通过EventBuilder其加入到EventBus中的缓存映射去,这样就不用再通过再运行时解析注解的订阅方法。所以APT的主要作用就是:1.扫描@Subscribe注解;2.支持提供argments 设置包名类名。3.根据订阅方法,以及提供的包名类名帮助我们去生成指定的这个类,并且将注册对象和订阅方法做映射;4.当我们注册某个对象时,由于apt帮我们已经生成并且放到缓存中,就不需要在运行时解析了。

三.源码解析

1.注册过程

代码分析:

1.EventBus 通过 EventBus.register(Object)方法来进行注册的。该方法会对 subscriber 进行解析,通过 SubscriberMethodFinder 的 findSubscriberMethods 方法将 subscriber 包含的所有声明了@Subscribe注解的方法的签名信息保存到内存中,当有消息被 Post 时就可以直接在内存中查找到目标方法了。

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
     //委托给subsciberMethodFinder执行查找
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

2.从 SubscriberMethod 包含的所有参数可以看出来,它包含了 @Subscribe 的参数信息以及对应的方法签名信息:

public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    //消息类型
    final Class<?> eventType;
    //消息优先级
    final int priority;
    //是否属于黏性消息
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;
	
    ···

}

3.SubscriberMethodFinder 会将每次的查找结果缓存到 METHOD_CACHE中,这对某些会先后经历多次注册和反注册操作的 subscriber 来说比较有用,因为每次查找可能需要依靠多次循环遍历和反射操作,会稍微有点消耗性能,但缓存也会占用一部分内存空间:

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) {
        return subscriberMethods;
    }

    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    if (subscriberMethods.isEmpty()) {
        //如果为空,说明没找到使用 @Subscribe 方法,那么 register 操作就是没有意义的,直接抛出异常
        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(subscriberClass) 方法

其主要逻辑是:

  1. 通过 prepareFindState() 方法从对象池 FIND_STATE_POOL 中获取空闲的 FindState 对象,如果不存在则初始化一个新的,并在使用过后通过 getMethodsAndRelease 方法将对象还给对象池。通过对象池来避免无限制地创建 FindState 对象,这也算做是一个优化点。FindState 用于在反射遍历过程中保存各种中间状态值
  2. 在不使用注解处理器的情况下 findState.subscriberInfosubscriberInfoIndexes默认都是等于 null,所以主要看 findUsingReflectionInSingleClass 方法即可,从该方法名可知是通过反射操作来进行解析的,解析结果会被存到 findState
  3. 因为父类声明的监听方法会被子类所继承,而解析过程是会从子类向其父类依次遍历的,所以在解析完子类后需要通过 findState.moveToSuperclass() 方法将下一个查找的 class 对象指向父类。
private static final int POOL_SIZE = 4;//对象池的大小
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    //步骤1
    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 {
            //步骤2
            findUsingReflectionInSingleClass(findState);
        }
        //步骤3查找父类
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    findState.recycle();//重置findState
    synchronized (FIND_STATE_POOL) {
        //回收 findState,尝试将之存到对象池中,如果对象池对象为空则将之前使用的加入到对象池中
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

//如果对象池中有可用的对象则取出来使用,否则的话就构建一个新的
private FindState prepareFindState() {
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}

这里来主要看下 findUsingReflectionInSingleClass 方法是如何完成反射操作的。如果解析到的方法签名不符合要求,则在开启了严格检查的情况下直接抛出异常;如果方法签名符合要求,则会将方法签名保存到subscriberMethods

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        //获取 clazz 包含的所有方法,不包含继承得来的方法
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        try {
            //获取 clazz 以及其父类的所有 public 方法
            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);
        }
        //由于 getDeclaredMethods() 都抛出异常了,就不再继续向下循环了,所以指定下次循环时忽略父类
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            //method 是 public 的,且不是 ABSTRACT、STATIC、BRIDGE、SYNTHETIC

            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {  //方法包含的参数个数是一
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) { //方法签名包含 Subscribe 注解
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        //校验通过后,就将 Subscribe 注解的配置信息及 method 方法签名保存起来
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                //因为 EventBus 只支持包含一个入参参数的注解函数,所以如果开启了严格的方法校验那么就抛出异常
                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)) {
            //如果 method 的方法签名不符合要求且开启了严格的方法校验那么就抛出异常
            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方法的一个重点是 findState.checkAdd方法。如果往简单了想,只要把 subscriber 每个声明了 Subscribe 注解的方法都给保存起来就可以了,可是还需要考虑一些特殊情况:

  1. Java 中的类是具有继承关系的,如果父类声明了 Subscribe 方法,那么就相当于子类也持有了该监听方法,那么子类在 register 后就需要拿到父类的所有 Subscribe 方法
  2. 如果子类继承并重写了父类的 Subscribe 方法,那么子类在 register 后就需要以自己重写后的方法为准,忽略父类的相应方法

checkAdd 方法就用于进行上述判断,可以在checkAdd断点查看执行流程,如果子类对父类有重写,第一次子类时走```

Object existing = anyMethodByEventType.put(eventType, method);

if (existing == null) {

return true;

}```,当使用父类时anyMethodByEventType.put(eventType, method);返回得非null,返回是子类得方法,然后我们就会走if (existing instanceof Method) {

if (!checkAddWithMethodSignature((Method) existing, eventType)) {

// Paranoia check

throw new IllegalStateException();

}

// Put any non-Method object to "consume" the existing Method

//会执行到这里,说明存在多个方法监听同个 Event,那么将 eventType

//对应的 value 置为 this

//避免多次检查,让其直接去执行 checkAddWithMethodSignature 方法

anyMethodByEventType.put(eventType, this);

}这段代码去检测,这段代码就会返回true,执行完之后,他就替换了 anyMethodByEventType.put(eventType, this);最后在执行时到checkAddWithMethodSignature(method, eventType);这段代码时,就会走到subscriberClassByMethodKey.put(methodKey, methodClassOld);这里然后替换,

//以 eventType 作为 key,method 或者 FindState 作为 value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//以 methodKey 作为 key,methodClass 作为 value
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();

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.

    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        //existing 等于 null 说明之前未解析到监听相同事件的方法,检查通过
        //因为大部分情况下监听者不会声明多个监听相同事件的方法,所以先进行这步检查效率上会比较高
        return true;
    } else { //existing 不等于 null 说明之前已经解析到同样监听这个事件的方法了

        if (existing instanceof Method) {
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                // Paranoia check
                throw new IllegalStateException();
            }
            // Put any non-Method object to "consume" the existing Method
            //会执行到这里,说明存在多个方法监听同个 Event,那么将 eventType 
            //对应的 value 置为 this
            //避免多次检查,让其直接去执行 checkAddWithMethodSignature 方法
            anyMethodByEventType.put(eventType, this);
        }
        //如果是父类,这里就会返回false
        return checkAddWithMethodSignature(method, eventType);
    }
}

private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    //以 methodName>eventTypeName 字符串作为 key
    //通过这个 key 来判断是否存在子类重写了父类方法的情况
    String methodKey = methodKeyBuilder.toString();
    //获取声明了 method 的类对应的 class 对象
    Class<?> methodClass = method.getDeclaringClass();

    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    //1. 如果 methodClassOld == null 为 true,说明 method 是第一次解析到,允许添加
    //2. 如果 methodClassOld.isAssignableFrom(methodClass) 为 true
    //2.1、说明 methodClassOld 是 methodClass 的父类,需要以子类重写的方法 method 为准,
    //      允许添加,实际上应该不存在这种情况,因为 EventBus 是从子类开始向父类进行遍历的
    //2.2、说明 methodClassOld 是 methodClass 是同个类,即 methodClass 声明了多个方法对同个事件进行监听 ,也允许添加
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        // Only add if not already found in a sub class
        //这里是同一个类返回的
        return true;
    } else {
        //重写返回的
        // Revert the put, old class is further down the class hierarchy
        //由于 EventBus 是从子类向父类进行解析
        //会执行到这里就说明之前已经解析到了相同 key 的方法,对应子类重写了父类方法的情况
        //此时需要以子类重写的方法 method 为准,所以又将 methodClassOld 重新设回去
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

进行上述操作后,就找到了 subscriber 包含的所有监听方法了,这些方法都会保存到 List<SubscriberMethod> 中。拿到所有方法后,register 方法就需要对 subscriber 及其所有监听方法进行归类了

归类的目的是既是为了方便后续操作也是为了提高效率。 因为可能同时存在多个 subscriber 声明了多个对同种类型消息的监听方法,那么就需要将每种消息类型和其当前的所有监听方法对应起来,提高消息的发送效率。而且在 subscriber 解除注册时,也需要将 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);
        }
    }
}

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

private final Map<Object, List<Class<?>>> typesBySubscriber;

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;//订阅方法中得参数类型
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //subscriptionsByEventType 以消息类型 eventType 作为 key,value 存储了所有对该
   // eventType 的订阅者,提高后续在发送消息时的效率
    // 比如 eventType为String,value就是所有eventType为String的,这个不针对某个对象,是所有的
    //都会添加它的value中去

    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            //说明某个 Subscriber 重复注册了
            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 以订阅者 subscriber 作为 key,
    //value 存储了其订阅的所有 eventType
    //用于向外提供某个类是否已注册的功能,
    //也方便后续在 unregister 时移除 subscriber 下的所有监听方法
    //比如key是MainActivity.class value是 所有订阅方法中得参数类型所有
    //,因为它是list 所以是所有,因为类型可以重复 
    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);
        }
    }
}

这里再用图表述下subscriptionsByEventType、typesBySubscriber这两个缓存。

2.消息发送过程

1.发送非粘性消息


/**
● For ThreadLocal, much faster to set (and get multiple values).
 */
final static class PostingThreadState {
 final List eventQueue = new ArrayList<>(); 
    boolean isPosting;
    boolean isMainThread;  
    Subscription subscription; 
    Object event; 
    boolean canceled; }
 private final ThreadLocal currentPostingThreadState = new ThreadLocal() {   
    @Override     protected PostingThreadState initialValue() {  
        return new PostingThreadState();     
    } 
};
/**
● Posts the given event to the event bus.
 */
public void post(Object event) {
 PostingThreadState postingState = currentPostingThreadState.get();
 List 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;
 }
 }
}

EventBus.getDefault().post(Any) 方法用于发送非黏性消息。EventBus 会通过 ThreadLocal 为每个发送消息的线程维护一个 PostingThreadState 对象,用于为每个线程维护一个消息队列及其它辅助参数,

每次 post 进来的消息都会先存到消息队列 eventQueue中,然后通过 while 循环进行处理,消息处理逻辑是通过 postSingleEvent方法来完成的

其主要逻辑是:

  1. 假设 EventA 继承于 EventB,那么当发送的消息类型是 EventA 时,就需要考虑 EventB 的监听方法是否可以接收到 EventA,即需要考虑消息类型是否具有继承关系
  2. 具有继承关系。此时就需要拿到 EventA 的所有父类型,然后根据 EventA 本身和其父类型关联到的所有监听方法依次进行消息发送
  3. 不具有继承关系。此时只需要向 EventA 的监听方法进行消息发送即可
  4. 如果发送的消息最终没有找到任何接收者,且 sendNoSubscriberEvent 为 true,那么就主动发送一个 NoSubscriberEvent 事件,用于向外通知消息没有找到任何接收者
  5. 监听方法之间可以设定消息处理的优先级高低,高优先级的方法可以通过调用 cancelEventDelivery 方法来拦截事件,不再继续向下发送。但只有在 POSTING 模式下才能拦截事件,因为只有在这个模式下才能保证监听方法是按照严格的先后顺序被执行的

最终,发送的消息都会通过 postToSubscription方法来完成,根据接收者方法不同的处理策略进行处理

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    //用于标记是否有找到消息的接收者
    boolean subscriptionFound = false;
    if (eventInheritance) {
        //步骤2
        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 {
        //步骤3
        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) {
            //步骤4
            post(new NoSubscriberEvent(this, event));
        }
    }
}

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;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            //步骤5
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

2.发送粘性消息

黏性消息的意义是为了使得在消息发出来后,即使是后续再进行 register 的 subscriber 也可以收到之前发送的消息,这需要将 @Subscribe 注解的 sticky 属性设为 true,即表明消息接收方希望接收黏性消息

EventBus.getDefault().postSticky(Any)方法就用于发送黏性消息。黏性事件会被保存到 stickyEvents 这个 Map 中,key 是 event 的 Class 对象,value 是 event 本身,这也说明对于同一类型的黏性消息来说,只会保存其最后一个消息。


private final Map<Class<?>, Object> stickyEvents;
/**
● Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
● event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
 */
public void postSticky(Object event) {
 synchronized (stickyEvents) {
 stickyEvents.put(event.getClass(), event);
 }
 // Should be posted after it is putted, in case the subscriber wants to remove immediately
 post(event);
}

对于一个黏性消息,会有两种不同的时机被 subscriber 接收到

  1. 调用 postSticky 方法时,被其现有的 subscriber 直接接收到,这种方式通过在 postSticky 方法里调用 post 方法来实现
  2. 调用 register 方法时,新添加的 subscriber 会判断 stickyEvents 中是否存在关联的 event 需要进行分发

这里主要看第二种情况。register 操作会在 subscribe 方法里完成黏性事件的分发。和 post 操作一样,发送黏性事件时也需要考虑 event 的继承关系

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

    ···

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

            //事件类型需要考虑其继承关系
            //因此需要判断每一个 stickyEvent 的父类型是否存在监听者,有的话就需要都进行回调
            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);
        }
    }
}

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
        // --> Strange corner case, which we don't take care of here.
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

3.移除黏性事件

移除指定的黏性事件可以通过以下方法来实现,都是用于将指定事件从 stickyEvents 中移除

/**
● Remove and gets the recent sticky event for the given event type.
● 
● @see #postSticky(Object)
 */
public  T removeStickyEvent(Class eventType) {  synchronized (stickyEvents) {  return eventType.cast(stickyEvents.remove(eventType));  } }
/**
● Removes the sticky event if it equals to the given event.
● 
● @return true if the events matched and the sticky event was removed.
 */
public boolean removeStickyEvent(Object event) {
 synchronized (stickyEvents) {
 Class<?> eventType = event.getClass();
 Object existingEvent = stickyEvents.get(eventType);
 if (event.equals(existingEvent)) {
     stickyEvents.remove(eventType);
     return true;
 } else {
     return false;
 }
 }
}

3.反注册过程

解除注册的目的是为了避免内存泄露,EventBus 使用了单例模式,如果不主动解除注册的话,EventBus 就会一直持有 subscriber。解除注册是通过 unregister方法来实现的,该方法逻辑也比较简单,只是将 subscriber 以及其关联的所有 method 对象从集合中移除而已

而此处虽然会将关于 subscriber 的信息均给移除掉,但是在 SubscriberMethodFinder 中的静态成员变量 METHOD_CACHE 依然会缓存着已经注册过的 subscriber 的信息,这也是为了在某些 subscriber 会先后多次注册 EventBus 时可以做到信息复用,避免多次循环反射

/**
 * 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) {
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

/**
 * Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber.
 */
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--;
            }
        }
    }
}