Eventbus 3.1.1 源码解析(一)

2019-03-03  本文已影响0人  LouisXWB

1、简介

Eventbus在项目中已经接入一段时间了,它是一个 Android 事件发布/订阅框架,作用是为了简化 Android 的事件传递。具体来说,就是它简化了 Android 组件之间的通信,包括四大组件之间、主线程与异步线程之间的通信,同时也解耦了发布者和订阅者的关系,避免了复杂、容易出错的依赖关系和生命周期问题。

下面通过Eventbus的基本使用方法开始,逐步深入对源码进行分析,让自己对这个框架有更深入的理解。

2、简介和基本使用

它主要由四个部分组成,发布者订阅者事件事件传递

EventBus 关系图

2.1 定义事件

我们需要提前定义事件Event的实体类,具体传递什么参数自己定义:

public static class MessageEvent { /* Additional fields if needed */ }

2.2 注册、解注册订阅者

很简单,我们只需要在订阅事件的地方,通过Eventbus进行注册即可:

EventBus.getDefault().register(this);

register是强引用,如果没有解注册,那么强引用会让对象一直被持有而无法回收,导致内存泄露,必须在不需要接收事件时取消注册:

EventBus.getDefault().unregister(this);

2.3 订阅事件

在需要接收事件的地方声明订阅方法,可以指定响应的线程类型、订阅方法的优先级和粘性,后面会具体分析:

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {/* Do something */};

2.4 发送事件

EventBus的post方法用于发送事件:

EventBus.getDefault().post(new MessageEvent());

还支持发送粘性事件:

EventBus.getDefault().postSticky(new MessageEvent());

3、分析源码

3.1 EventBus getDefault()

通过懒汉模式获取EventBus实例:

/** Convenience singleton for apps using a process-wide EventBus instance. */
    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;
    }

如果不存在,则通过构造函数来初始化实例,传入的DEFAULT_BUILDER是一个默认创建好的EventBusBuilder实例,它包含了创建EventBus实例的一系列自定义参数,通过它我们就可以自定义自己的Eventbus实例,各参数的具体含义如下。

/**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        //日志处理器,默认为android.util.Log,也可以自己设置其他的日志处理器
        subscriptionsByEventType = new HashMap<>();
       //Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
       //key为事件的类型,value是所有订阅该事件的List集合
        typesBySubscriber = new HashMap<>();
        // Map<Object, List<Class<?>>> typesBySubscriber
        //订阅者对应订阅事件的集合,key是订阅者,value是订阅者订阅的事件集合
        stickyEvents = new ConcurrentHashMap<>();
        //保存粘性事件的集合
        mainThreadSupport = builder.getMainThreadSupport();
        // 主线程接口,可以自定义,默认是Android定义的主线程
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        //主线程发送器
        backgroundPoster = new BackgroundPoster(this);
        //后台线程发送器
        asyncPoster = new AsyncPoster(this);
        //异步线程发送器
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        //索引数量,为了提高优化性能,3.0以后提供索引,后面再详细介绍
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        //用于查找订阅者方法 
        logSubscriberExceptions = builder.logSubscriberExceptions;
        //是否打印订阅者调用订阅函数异常的日志,默认为true
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        //是否打印没有订阅者订阅事件的日志,默认为true
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        //订阅者调用订阅函数异常时,是否发送SubscriberExceptionEvent事件,默认为true
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        //没有订阅者订阅事件事,是否发送NoSubscriberEvent事件,默认为true
        throwSubscriberException = builder.throwSubscriberException;
        //是否抛出SubscriberException异常,默认为false
        eventInheritance = builder.eventInheritance;
        //是否支持事件继承,默认为 true
        executorService = builder.executorService;
        //线程池
    }

3.2 EventBus register(Object subscriber)

注册订阅者,如果需要订阅事件,必须通过调用register(Object subscriber)方法来注册,才可接收到目标事件

/**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    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,它包含了订阅者方法的一些参数,只在内部给Eventbus和生成的索引来使用,这些参数在编写订阅者方法时就可以自定义:

/** Used internally by EventBus and generated subscriber indexes. */
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.2.1 SubscriberMethodFinder findSubscriberMethods(Class<?> subscriberClass)

SubscriberMethodFinder: 查找订阅方法的类
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass):查找订阅者所有的订阅方法

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
       List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
       //Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
       //获取订阅者对应的订阅方法集合的缓存
       if (subscriberMethods != null) {
           //缓存不为null则直接返回
           return subscriberMethods;
       }
       
       //ignoreGeneratedIndex:是否忽略注解器生成的索引(索引后面会介绍)
       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");
           //找不到则抛出”no public methods with the @Subscribe annotation“异常
       } else {
           METHOD_CACHE.put(subscriberClass, subscriberMethods);
           //将订阅者-订阅者方法放进缓存的map集合METHOD_CACHE
           return subscriberMethods;
       }
   }

内部获取订阅者方法的实现有两种

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        //初始化保存和校验订阅方法的类FindState
        while (findState.clazz != null) {
            //通过反射获取订阅方法
            findUsingReflectionInSingleClass(findState);
            //查找订阅者的父类
            findState.moveToSuperclass();
        }
        //返回查找到的订阅方法集合
        return getMethodsAndRelease(findState);
    }
//用于订阅方法的保存和校验
class FindState {
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);
        boolean checkAdd(Method method, Class<?> eventType){...}
        boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {...}
        void moveToSuperclass() {...}
}

FindState:用于订阅方法的保存和校验
通过SubscriberMethodFinder的prepareFindState()方法获得:

//FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
//int POOL_SIZE = 4;
//FIND_STATE_POOL为FindState的缓存池,FindState就可循环利用,避免重复创建过多对象,节省内存。
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();
    }

接下来我们看看如何通过反射获取订阅方法
void findUsingReflectionInSingleClass(FindState findState)

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
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
               // 方法的类型参数只能有一个
                if (parameterTypes.length == 1) {
                    //通过@Subscribe 注解获得Subscribe注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        //校验方法
                        if (findState.checkAdd(method, eventType)) {
                            //解析注解的线程类型
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //创建订阅方法对象,并保存至findState的subscriberMethods集合
                            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");
            }
        }
    }

我们已经一步步了解完如何通过反射获取订阅方法集合,接下来我们要讲第二种获取订阅方法的方式,通过索引获取订阅方法:findUsingInfo(Class<?> subscriberClass)

我们先了解一下索引是什么?

Eventbus3.0之前使用的是运行时注解,获取订阅方法都是通过java的反射机制,而java的反射机制是非常耗费性能的,详细可见这篇文章# java反射的性能问题

因为反射机制是通过对类的所有方法进行扫描,在一些对低端、对性能要求高的真机上,很容易因为性能消耗过高导致体验不佳。

Eventbus3.0开始就使用编译时注解,通过EventBusAnnotationProcessor注解器在编译时就读取@Subscribe()注解并解析,即在Java编译成.class文件时就进行操作,处理@Subscribe所包含的信息,然后生成SubscriberInfoIndex索引类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快。

在Eventbus的demo(EventBusPerformance)中,build->generated->source->apt中我们可以找到MyEventBusIndex索引类,我们对它进行分析一下:

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscriberClassEventBusAsync.class,
                true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventAsync", TestEvent.class, ThreadMode.ASYNC),
        }));

        putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusMain.class,
                true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventMainThread", TestEvent.class, ThreadMode.MAIN),
        }));

        putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusBackground.class,
                true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventBackgroundThread", TestEvent.class, ThreadMode.BACKGROUND),
        }));

        putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusMainOrdered.class,
                true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", TestEvent.class, ThreadMode.MAIN_ORDERED),
        }));

        putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.SubscribeClassEventBusDefault.class,
                true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", TestEvent.class),
        }));

        putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventMainThread", TestFinishedEvent.class, ThreadMode.MAIN),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

SUBSCRIBER_INDEX :订阅者对应订阅者信息的map集合
SubscriberInfo:注解器生成的接口

/** Base class for generated index classes created by annotation processing. */
public interface SubscriberInfo {
    Class<?> getSubscriberClass();
    //获取订阅者类
    SubscriberMethod[] getSubscriberMethods();
    //获取订阅方法数组
    SubscriberInfo getSuperSubscriberInfo();
    //获取父类订阅信息
    boolean shouldCheckSuperclass();
    //是否检查父类
}

具体实现如下:

/** Base class for generated subscriber meta info classes created by annotation processing. */
public abstract class AbstractSubscriberInfo implements SubscriberInfo {
    private final Class subscriberClass;
    private final Class<? extends SubscriberInfo> superSubscriberInfoClass;
    private final boolean shouldCheckSuperclass;

    protected AbstractSubscriberInfo(Class subscriberClass, Class<? extends SubscriberInfo> superSubscriberInfoClass,
                                     boolean shouldCheckSuperclass) {
        this.subscriberClass = subscriberClass;
        this.superSubscriberInfoClass = superSubscriberInfoClass;
        this.shouldCheckSuperclass = shouldCheckSuperclass;
    }

    @Override
    public Class getSubscriberClass() {
        return subscriberClass;
    }

    @Override
    public SubscriberInfo getSuperSubscriberInfo() {
        if(superSubscriberInfoClass == null) {
            return null;
        }
        try {
            return superSubscriberInfoClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean shouldCheckSuperclass() {
        return shouldCheckSuperclass;
    }

    protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType) {
        return createSubscriberMethod(methodName, eventType, ThreadMode.POSTING, 0, false);
    }

    protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode) {
        return createSubscriberMethod(methodName, eventType, threadMode, 0, false);
    }

    protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode,
                                                      int priority, boolean sticky) {
        try {
            //实际上仍是通过反射获取定义的相关参数,然后生成订阅方法SubscriberMethod,只不过这是在编译时就生成了
            Method method = subscriberClass.getDeclaredMethod(methodName, eventType);
            return new SubscriberMethod(method, eventType, threadMode, priority, sticky);
        } catch (NoSuchMethodException e) {
            throw new EventBusException("Could not find subscriber method in " + subscriberClass +
                    ". Maybe a missing ProGuard rule?", e);
        }
    }
}
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {

    private final SubscriberMethodInfo[] methodInfos;

    public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {
        super(subscriberClass, null, shouldCheckSuperclass);
        this.methodInfos = methodInfos;
    }

    @Override
    public synchronized SubscriberMethod[] getSubscriberMethods() {
        //根据抽象父类的反射方法生成订阅方法对象SubscriberMethod
        //返回所有的订阅方法
        int length = methodInfos.length;
        SubscriberMethod[] methods = new SubscriberMethod[length];
        for (int i = 0; i < length; i++) {
            SubscriberMethodInfo info = methodInfos[i];
            methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
                    info.priority, info.sticky);
        }
        return methods;
    }
}

到这里,我们已经知道索引的具体含义,包括索引怎么生成订阅方法的底层逻辑,接下来我们继续回到通过索引获取订阅方法的上层函数:findUsingInfo(Class<?> subscriberClass)

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //获取订阅者信息,下面会分析getSubscriberInfo(findState)
            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集合进行保存
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                //根据索引获取不到订阅者信息,则通过反射获取
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

分析获取订阅者信息getSubscriberInfo(findState)

private SubscriberInfo getSubscriberInfo(FindState findState) {
        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
             //获取父类的订阅信息并返回
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
        if (subscriberInfoIndexes != null) {
            //根据订阅类class,查找对应的索引类,找到后再通过索引类维护的Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX
           //返回对应的订阅信息
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

至此,我们已经分析完获取订阅方法的详细逻辑,包括使用反射获取索引获取。接下来继续分析找到全部订阅方法后,注册流程的下一步subscribe()方法的实现。

3.2.2 subscribe(Object subscriber, SubscriberMethod subscriberMethod)

// Must be called in synchronized block 必须在同步代码块里调用
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //根据事件类型获取所有订阅该事件的Subscription集合,Subscription包括订阅者和订阅方法
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            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 > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //将订阅者与对应的订阅事件存放到Map<Object, List<Class<?>>> typesBySubscriber;
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        //如果订阅方法支持sticky
        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();
                //遍历粘性sticky事件
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    // 判断eventType是否为candidateEventType,或者是candidateEventType的父类
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        // 得到eventType的子类和eventType 对应的事件
                        Object stickyEvent = entry.getValue();
                        //// 立即发送粘性事件到订阅者newSubscription
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                //不是事件继承的,则直接发送粘性事件
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

到这里已经完成register()注册流程的全部解析,流程图(引用自EventBus 3.0 源码分析)如下

register-flow-chart.png
上一篇下一篇

猜你喜欢

热点阅读