Android-EventBus底层原理浅析(一)

好不啰嗦,直奔主题,我打算用三篇文章来简单跟大家一起过一遍eventbus框架的源码,三篇分别分为注册、通知、注销三个主要内容点来展开,所以这篇咱们来了解一下注册,看看eventbus是怎样处理我们的订阅者的。
(本文建立在3.1.1版本下)
首先上个注册方式

EventBus.getDefault().register(this)

然后生成我们的订阅方法(就是接收数据的方法)

@Subscribe(threadMode = ThreadMode.MAIN)
    fun onGetMessage(messageAEvent: MessageEvent.Companion.MessageAEvent){
    }

是的,这样就算是注册好了,当其他界面调用了post方法后就能接收到通知数据了(传入了一样的参数类型,比如栗子的MessageAEvent对象),比如这样

EventBus.getDefault().post(MessageEvent.Companion.MessageAEvent(1))

那为啥这么流弊的就可以接收到了呢,而且还是两个不想关的界面,只需要一个条件,就是使用相同的参数类型,简直了,不要急,让我们一步步开始迷失在源码的海洋里吧。

首先我们看一下getDefault()吧

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

哦豁,一个单例生成了EventBus对象,课外知识(敲黑板)这个单例模式叫《双重校验锁单例模式》,单例模式可不止是判断一下是否为空一种写法,出于多线程安全方面需要这样写,关键词synchronized 、volatile ,有兴趣的筒子网上搜单例模式各种写法的利弊,开开眼界哈,那我们继续,new EventBus()

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

/**
     * 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();
        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;
    }

哦豁,传入了一个builder,课外知识(敲黑板)这种模式叫建造者模式,日常生活使用超多,比如创建Dialog,老规矩,有兴趣自己搜哈,那回到一开始 ,我们要去.register(this)看看了

public void register(Object subscriber) {
   1     Class<?> subscriberClass = subscriber.getClass();
   2     List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
   3            subscribe(subscriber, subscriberMethod);
            }
        }
    }

1:获得我们的订阅类(activity之类的)
2:获得我们订阅类里的订阅方法(加了注解用来接收通知的那个方法(可能多个所以是list))
3:(敲黑板)将两者绑定起来,提供给通知的时候调用

好讲完了,下课。
说笑的说笑的,收起你们的40米大刀,我们继续,继续!现在我们要去2那里,findSubscriberMethods

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

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
  1      List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
  2          subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
  3         METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

1:根据订阅类去搜索出订阅方法(第一次所以为空)
2:根据ignoreGeneratedIndex值判断调用什么方法去寻找订阅的方法s(是在builder里面设置的,表示是否使用生成的APT代码去优化查找事件的过程,如果项目中没有接入EventBus的APT,也可以将其设置为false)
3:将查询到的订阅方法保存起来
好的,接下来我们要看findUsingInfo(subscriberClass)

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
  1      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();
        }
  4      return getMethodsAndRelease(findState);
    }

1:根据订阅类初始化一个FindState对象(主要作用是放东西的)
2:第一次进入findState.subscriberInfo是空的,还没查呢能有东西嘛,所以会执行findUsingReflectionInSingleClass,通过反射查找订阅方法
3:继续遍历父类(所以你子类跟父类的订阅方法,都是有效的)
4:在findState拿出查找到的订阅方法返回出去,并将findState释放掉

ok,接下来要看的是2,findUsingReflectionInSingleClass(findState)

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
 1            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();
 2           if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
 3               if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
 4                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
 5                      if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
 6                           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");
            }
        }
    }

别看这个方法pia一大堆,其实也就那回事
1:通过反射获取所有的方法
2、3、4:根据条件去判断合适的方法,首先是要Public的,然后不能有abstract、static等鬼东西,接着只能有一个参数,接着需要有@Subscribe注解
5:校验是否可以加入到list中
6:生成一个合格的SubscriberMethod对象(订阅方法)然后add进findState的subscriberMethods集合里

好了,到这里过,我们已经把订阅类里的合格的订阅方法查了出来,并丢进了findState类里,下一步我们要去上面上面的getMethodsAndRelease(findState)

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

这里没啥,就是新建个list,把刚刚查的方法丢进去,然后返回出去并让findState滚
接下来我们回到外面的register方法,诶别翻了,我给你们贴出来吧

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

此时subscriberMethods 已经有值了,接下来要去走subscribe(subscriber, subscriberMethod)

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
 1            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;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
 2            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

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

这里可能会把你绕晕,解释之前我先说两个理论知识
首先就是,我们的订阅类跟订阅方法是一对多的关系,而我们订阅方法里传的参数跟订阅类也是一对多的关系(比如我一个EventObject,任何一个类都能拿去使用到订阅方法里当参数),所以我们的关系是:
参数:类:方法 = 1:N:N,应该可以理解吧,所以这个方法里subscriptionsByEventType对象是这样的:

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

1:以eventType(参数)为key,subscriptions(Subscription的集合)为value保存起来
2:以subscriber(订阅类)为key,subscribedEvents(参数的集合)为value保存起来
3:如果是粘性事件的话(不打算讲这个,因为又可以单独开一篇,简单来讲就是当你post出消息之后,订阅者才订阅,如果设置了粘性事件,那他是可以收到的,一般我们都是先执行好了订阅者,再post消息对吧,这个反着来,也可以收到消息就对了)
这个方法就是一个数据绑定的地方(把他们按照我说的这个关系给存放了起来,提供给post的时候使用,至于怎么使用的第二篇说)

来总结一下大致流程

1 首先获得我们的订阅者对象
2 利用反射的原理去获取我们订阅者的方法,之后根据条件去寻找合格的方法并保存(public、非静态、一个传参、拥有@Subscribe注解等)
3 遍历完子类会继续遍历父类,所以子类父类的订阅方法都会生效
4 遍历我们寻找到的合格的方法集合,让它们跟着订阅者一起去做一个数据存放(绑定)的流程,其中一个订阅者可以拥有多个订阅方法,一个参数类型(event)也可以有多个订阅者使用
5 注册流程结束

ok,走到这儿我们的第一部分register已经完成了他的工作,接下来就是触发post的时候,下一篇见。

推荐阅读更多精彩内容