EventBus工作原理

EventBus大家一定不陌生,组件之间传递消息只要post一下,对方就能收到,还可以任意切换线程,究竟内部是如何完成的呢?今天就一眼究竟。

一、首先把简单的用法贴一下:

1.注册,在需要接受消息的组件内进行注册(在组件创建的位置进行注册)

EventBus.getDefault().register(this);

2.编写处理消息的方法

@Subscribe

public void getInfo(String str){

    //发送的消息会在这里进行接受

}

3.发送消息在需要发送消息的组件内做如下操作

EventBus.getDefault().post("哈罗呀");这样就可以在2步的方法内接受到我们发送的消息,就是这么神奇。

4.最后一步别忘记在注册的组建内onDestory的时候对EventBus进行反注册

EventBus.getDefault().unregister(this);

二、那我们来看一下短短的几句代码背后都为我们做了些什么

1、EventBus.getDefault(),代码点进去不出意外的单例模式

public static EventBus getDefault() {

if (defaultInstance == null) {

synchronized (EventBus.class) {

if (defaultInstance == null) {

defaultInstance = new EventBus();

}

}

}

return defaultInstance;}

标准的懒汉式单例模式,我们把完整的实例对象和构造函数贴出来看一下

static volatile EventBus defaultInstance;

public EventBus() { this(DEFAULT_BUILDER);}

有没有发现哪里不对劲,标准的单例类的实例和构造方法不是应该都是private修饰的吗,为什么会是这样呢?其实EventBus提供了两种方式来实例化Eventbus,首先就是通过单例,我们来看下这个过程

public EventBus() {

    this(DEFAULT_BUILDER);

}

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

单例中new 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;

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;

}

我们看到这里采用建造者模式帮我们初始化了一些属性,那么这些属性可不可以自己做一些特殊的定制呢?我们马上看下上面说到的第二种获取EventBus的方法

public static EventBusBuilder builder() {

    return new EventBusBuilder();

}

看见没有返回一个EventBusBuilder,想到了什么,建造者模式,那么我们就可以通过这个EventBusBuilder去设置上面第一种方法给我们的默认属性,并最终通过EventBusBuilder.builder()构造出一个根据我们需求自定义的EventBus实例

EventBusBuilder builder = EventBus.builder();

builder.eventInheritance(true);

builder.installDefaultEventBus();

。。。。。。

builder.sendNoSubscriberEvent(true);

EventBus eventBus = builder.build();

上面初始化的一些map和属性我们下面用到再说。

2.我们来看下注册里面帮我们做了哪些事情EventBus.getDefault().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);

}

}}

register的时候我们把当前的Activity传进去,我们看findSubscriberMethods方法里面都做了什么

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {

List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);//(1)

if (subscriberMethods != null) {

return subscriberMethods;

}

if (ignoreGeneratedIndex) { //(2)

subscriberMethods = findUsingReflection(subscriberClass);

} else {

subscriberMethods = findUsingInfo(subscriberClass);//(3)

}

if (subscriberMethods.isEmpty()) {

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;

}

}

(1)首先去缓存中去查找,有的话直接返回,没有的话继续,我们是第一次进行注册缓存里面肯定是空的,我们继续向下看我们看(2)处的条件判断,ignoreGeneratedIndex这个是什么时候赋值的,如果大家细致的看过获取EventBus实例的时候那堆默认的build属性,就应该还有印象,我们看一下

subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex);

继续看下builder.ignoreGeneratedIndex中的赋值

/** Forces the use of reflection even if there's a generated index (default: false). */public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) { this.ignoreGeneratedIndex = ignoreGeneratedIndex; return this;}

看注释,默认这个值是false,ok我们继续返回上面的if条件判断

那上面(2)中的条件判断应该走的是else分支findUsingInfo(subscriberClass)我们看下这个方法帮我们做了什么

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {

FindState findState = prepareFindState();//1

findState.initForSubscriber(subscriberClass);

while (findState.clazz != null) {//这个在初始化FindState 将订阅类的class赋值给findState.clazz,这个地方为true

findState.subscriberInfo = getSubscriberInfo(findState);//2

if (findState.subscriberInfo != null) {//3

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

}

return getMethodsAndRelease(findState);

}

1帮我们初始化了一个FindState,我们来看下FindState类

final List<SubscriberMethod> subscriberMethods = new ArrayList<>();

final Map<Class, Object> anyMethodByEventType = new HashMap<>();

final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();


SubscriberMethod记录了订阅者类及其中的订阅方法,anyMethodByEventType 

2看一下getSubscriberInfo方法做了哪些工作

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

for (SubscriberInfoIndex index : subscriberInfoIndexes)

{

SubscriberInfo info = index.getSubscriberInfo(findState.clazz);

if (info != null) {

return info;

}

}

}

return null;

}

我们在初始化FindState的时候将findState.subscriberInfo置为null,所以直接走第二个if,看一下subscriberInfoIndexes 这个是在什么时候赋值的

EventBus(EventBusBuilder builder) {

subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex);

}

List<SubscriberInfoIndex> subscriberInfoIndexes;

最后发现还是在构造EventBus实例的时候赋值的,但是我们会发现builder.subscriberInfoIndexes是个空值,还有对subscriberInfoIndexes进行初始化,我们回到getSubscriberInfo方法中第二个if也没能够走进去,最后只好return null,我们再回到上面的findUsingInfo方法中的3,由于getSubscriberInfo返回null,因此这个判断语句走else分支,我们看下这个方法中做了什么findUsingReflectionInSingleClass(findState);方法太长,我们把核心代码贴出来看一下

......

if (subscribeAnnotation != null) {

Class<?> eventType = parameterTypes[0];

if (findState.checkAdd(method, eventType)) {

ThreadMode threadMode = subscribeAnnotation.threadMode();

findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));

}

}

......

上面省略号代码是通过反射获取注册类中的所有方法,中间代码主要把@Subscribe标注的方法找出来,并获取注册的参数信息如:标注的线程、事件的优先级、是否是粘性事件等,用获取的这些信息构建一个SubscriberMethod对象并添加到findState.subscriberMethods集合中,最后findUsingInfo方法return getMethodsAndRelease(findState),这个方法主要是将我们构造的FindState放入FIND_STATE_POOL数组中,方便下次使用,ok到此findUsingInfo就全部分析完成,通过这个方法我们最终得到了List<SubscriberMethod>,我们继续往回翻到findSubscriberMethods这个方法中的(3)位置,向下走判断subscriberMethods这个是不是空,通过上面的分析,这个是不为空的,将我们的subscriberMethods和subscriberClass放到METHOD_CACHE中,下次相同的subscriberClass类register的时候就不同走下面一大堆的方法了,直接在METHOD_CACHE中查找并返回就好了,findSubscriberMethods方法的最后将我们获取的subscriberMethods返回,我们回到register方法中

synchronized (this) {

for (SubscriberMethod subscriberMethod : subscriberMethods) {

subscribe(subscriber, subscriberMethod); }}

遍历subscriberMethods并相应在走subscribe方法,我们去看看,subscribe主要做一些绑定工作,具体代码就不贴了,把其中几个关键的容器介绍下

subscriptionsByEventType保存了相应方法的参数的.class对象为key,CopyOnWriteArrayList<Subscription> subscriptions为value的Map中

Subscription中保存了Subscriber; subscriberMethod;两个对象

List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);

if (subscribedEvents == null) {

subscribedEvents = new ArrayList<>();

typesBySubscriber.put(subscriber, subscribedEvents);

}

subscribedEvents.add(eventType);

typesBySubscriber保存了订阅者为key,以及订阅者内部相应@Subscribe标注的方法的参数的class对象的集合为value的Map

@Subscribe

public void getInfo(String str){

}

subscribedEvents.add(eventType);这个eventType就是上面String的class对象

到此我们把subscribe方法中绑定的集中关系简单介绍了下,subscribe方法中还做了粘性事件相关的绑定我们暂且先不做了解,到此我们把订阅相关的方法介绍到此结束。

三.我们看下发送事件之后都做了哪些工作,EventBus.getDefault().post("哈罗呀");

public void post(Object event) {

PostingThreadState postingState = currentPostingThreadState.get();

List<Object> eventQueue = postingState.eventQueue;//1

eventQueue.add(event);//2

if (!postingState.isPosting) {//3

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);//4

}

}

finally {

postingState.isPosting = false;

postingState.isMainThread = false;

}

}

}

currentPostingThreadState我们看到这是一个ThreadLocal,关于ThreadLocal不了解的可以了解下,这里就不多做介绍,就是一个本地线程副本变量工具类,PostingThreadState类我们上面应该也看到过,他里面记录了一些当前线程里面事件的搜集和发送状态,我们看到1处获取了PostingThreadState中维护的事件队列,2将要发送的事件放入到队列中,往下走到3的if判断中,我们看到postingState.isPosting默认是false,走进判断分支中,我们直接看关键的第四步,这里是一个while循环,会吧队列中的事件发送的同时,将事件从队列中删除,我们看下postSingleEvent方法

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {

Class<?> eventClass = event.getClass();

boolean subscriptionFound = false;

if (eventInheritance) {//1

List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);//2

int countTypes = eventTypes.size();

for (int h = 0; h < countTypes; h++) {//3

Class<?> clazz = eventTypes.get(h);

subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);

}

} else {

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

post(new NoSubscriberEvent(this, event));

}

}

}

postSingleEvent方法中的1,eventInheritance这个值是在构建EventBus的时候初始化的boolean eventInheritance =true;默认为true,走到2lookupAllEventTypes方法中,代码补贴了,直接看一下注释

Looks up all Class objects including super classes and interfaces. Should also work for interfaces.

查找所有类对象,包括超类和接口

就是讲发送事件的class对象,包括父类和接口的所有的,放入集合中并返回List<Class<?>> eventTypes,还做了一件事情,放入到eventTypesCache中,下次同样的就直接返回。ok我们回到postSingleEvent方法的3处继续,这里for循环遍历我们刚才获取的eventTypes集合,并调用postSingleEventForEventType方法,我们看一下postSingleEventForEventType方法中的关键方法

postToSubscription(subscription, event, postingState.isMainThread);

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 {

mainThreadPoster.enqueue(subscription, event);

}

break;

case MAIN_ORDERED:

if (mainThreadPoster != null) {

mainThreadPoster.enqueue(subscription, event);

} else {

// temporary: technically not correct as poster not decoupled from subscriber invokeSubscriber(subscription, event);

}

break;

case BACKGROUND:

if (isMainThread) {

backgroundPoster.enqueue(subscription, event);

} else {

invokeSubscriber(subscription, event);

}

break;

case ASYNC:

asyncPoster.enqueue(subscription, event);

break;

default:

throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);

}

}

我们看到postToSubscription方法中会根据我们设置的不同的线程进行不同的处理,其实做的操作都是反射调用注册者内响应的方法,我们随便看一下方法具体的实现

void invokeSubscriber(Subscription subscription, Object event) {

try {

subscription.subscriberMethod.method.invoke(subscription.subscriber, event);//1

} catch (InvocationTargetException e) {

handleSubscriberException(subscription, event, e.getCause());

} catch (IllegalAccessException e)

{

throw new IllegalStateException("Unexpected exception", e);

}

}

invokeSubscriber方法中的1处,这个我们应该不陌生就是反射调用方法把我们发送的事件event传过去,到此Eventbus基本流程就全部介绍完毕,如有不正确的地方欢迎评论区讨论。

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

推荐阅读更多精彩内容

  • 项目到了一定阶段会出现一种甜蜜的负担:业务的不断发展与人员的流动性越来越大,代码维护与测试回归流程越来越繁琐。这个...
    fdacc6a1e764阅读 3,163评论 0 6
  • 先吐槽一下博客园的MarkDown编辑器,推出的时候还很高兴博客园支持MarkDown了,试用了下发现支持不完善就...
    Ten_Minutes阅读 557评论 0 2
  • EventBus基本使用 EventBus基于观察者模式的Android事件分发总线。 从这个图可以看出,Even...
    顾氏名清明阅读 616评论 0 1
  • BroadcastReceiver ​ 本质上是一个监听器,分为接收者和发送者。用于监听(接收)应用发出的广播...
    dw_0920阅读 244评论 0 0
  • 观察者模式 自己对观察者模式的理解:定义:Define a one-to-many dependency betw...
    安仔夏天勤奋阅读 1,134评论 1 3