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的时候,下一篇见。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容