EventBus源码学习,2024年最新java.面试

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

// 如果该方法被@subscribe注解会走该分支

Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);

if (subscribeAnnotation != null) {

// 获取传入的对象的Class

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

}

}

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

}

}

}

findUsingReflectionInSingleClass()方法首先通过反射去拿到当前注册对象的所有的方法,然后去进行遍历,并进行第一次过滤,只针对修饰符是Public的方法,之后进行了第二次过滤,判断了方法的参数的个数是不是只有一个,如果满足,才去进一步的获取被@subscribe注解的方法。

然后调用

findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()))这行代码,new了一个SubscriberMethod()对象,传入参数,并添加到 findState.subscriberMethods的集合中去.

static class FindState {

final List subscriberMethods = new ArrayList<>();

}

之后,findUsingInfo()getMethodsAndRelease(findState)方法回去获取刚刚设置的findStatesubscriberMethods集合,并把它return出去。代码如下:

private List getMethodsAndRelease(FindState findState) {

// 对subscriberMethods进行了赋值,return出去

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

}

步骤1总结:至此,以上就是EventBus获取一个注册对象的所有的被@subscribe注解的方法的集合的一个过程。该过程的主要方法流程为:

(1) subscriberMethodFinder.findSubscriberMethods()

(2) findUsingInfo()

(3) findUsingReflectionInSingleClass()

步骤2

for (SubscriberMethod subscriberMethod : subscriberMethods) {

subscribe(subscriber, subscriberMethod);

}

通过步骤1,我们已经拿到了注册对象的所有的被@subscribe注解的方法的集合的。现在我们看看subscribe()都做了哪些 操作。

我们不妨想想,如果我们要去做subscribe()时,我们要考虑哪些问题,第一个问题是,要判断一下这些方法是不是已经注册过该事件了要不要考虑方法名是不是相同的问题。第二个问题是一个注册对象中有多个方法注册了该事件,我们该怎么保存这些方法,比如说事件类型是String,一个Activity里有两个方法注册了该事件,分别是onEvent1和onEvent2,那我是不是应该用一个Map集合,以事件类型为key,把onEvent1和onEvent2放到一个List集合中,把该List集合作为value。

subscribe()方法:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

// 拿到事件event类型,比如是String或者自定义的对象

Class<?> eventType = subscriberMethod.eventType;

// Subscription将注册对象和subscriberMethod 做为参数传入

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

// subscriptionsByEventType是一个Map集合,key是事件类型,验证了我上面的猜想

CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);

// 如果subscriptions是null,则new出一个CopyOnWriteArrayList,并且往Map集合中添加

if (subscriptions == null) {

subscriptions = new CopyOnWriteArrayList<>();

subscriptionsByEventType.put(eventType, subscriptions);

} else {

// 这里做了if语句判断,判断一下List集合中是否存在,存在就抛异常

// 如果不存在?怎么没有add操作? 保持疑问

if (subscriptions.contains(newSubscription)) {

throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "

  • eventType);

}

}

以上的操作验证了我之前的猜想,通过if (subscriptions.contains(newSubscription)) 这个if语句判断 是否发生了重复注册,注意这里重复注册的含义是 事件类型一致,以及方法名也一致。

接下来我们看看如果一个注册对象重复注册了事件Event(方法名不能一致),优先级priority是如何设置的

int size = subscriptions.size();

for (int i = 0; i <= size; i++) {

// 这里判断subscriberMethod的优先级是否是大于集合中的subscriberMethod的优先级,如果是,把newSubscription插进去

// 这也表明了subscription中priority大的在前,这样在事件分发时就会先获取。

if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {

subscriptions.add(i, newSubscription);

break;

}

}

if语句的条件subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) ,保证了subscription中priority大的在前。同时i == size 这个条件也保证了priority小的也会添加到subscriptions集合中去

紧接着我们看看EventBus是如何处理粘性事件的:

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

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

}

}

}

注意以上代码有四行比较重要的注释信息。大致的意思是必须考虑eventType所有子类的现有粘性事件,在迭代的过程中,所有的event可能会因为大量的sticky events变得低效,为了使得查询变得高效应该改变数据结构。

isAssignableFrom方法的意思是判断candidateEventType是不是eventType的子类或者子接口,如果postSticky()的参数是子Event,那么@Subscribe注解方法中的参数是父Event也可以接收到此消息。

拿到粘性Event后,调用了checkPostStickyEventToSubscription()方法,改方法内部方法内部调用了postToSubscription()

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, Looper.getMainLooper() == Looper.myLooper());

}

}

步骤2总结:至此,EventBus的注册操作已经全部分析完了,需要注意的是,粘性事件是在subscribe中进行post的

(二) 发送事件:EventBus.getDefault().post(xxx);

普通Event

public void post(Object event) {

PostingThreadState postingState = currentPostingThreadState.get();

List eventQueue = postingState.eventQueue;

// 将Event添加到List集合中去

eventQueue.add(event);

if (!postingState.isPosting) {

postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();

postingState.isPosting = true;

if (postingState.canceled) {

throw new EventBusException(“Internal error. Abort state was not reset”);

}

try {

// 遍历这个list集合,条件是集合是否是空的

while (!eventQueue.isEmpty()) {

postSingleEvent(eventQueue.remove(0), postingState);

}

} finally {

postingState.isPosting = false;

postingState.isMainThread = false;

}

}

}

首先将当前的 Event添加到eventQueue中去,并且while循环,处理post每一个Event事件,调用的是 postSingleEvent(eventQueue.remove(0), postingState):

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

// 获取Event的Class对象

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

boolean subscriptionFound = false;

// eventInheritance初始化的时候值为true,所以会走该分支

if (eventInheritance) {

// 获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。

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 {

subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);

}

}

这里lookupAllEventTypes()方法也是为了获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。之后是 for循环获取到的Class对象集合,调用postSingleEventForEventType()方法:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {

CopyOnWriteArrayList subscriptions;

synchronized (this) {

// subscriptionsByEventType该map是在subscribe()方法中进行了put操作

subscriptions = subscriptionsByEventType.get(eventClass);

}

if (subscriptions != null && !subscriptions.isEmpty()) {

for (Subscription subscription : subscriptions) {

postingState.event = event;

postingState.subscription = subscription;

boolean aborted = false;

try {

// 进行for循环并调用了postToSubscription()

postToSubscription(subscription, event, postingState.isMainThread);

aborted = postingState.canceled;

} finally {

postingState.event = null;

postingState.subscription = null;

postingState.canceled = false;

}

if (aborted) {

break;

}

}

return true;

}

return false;

}

postSingleEventForEventType()方法,主要是获取Event的Class对象所对应的一个List集合,集合的对象是Subscription参数。subscriptionsByEventType对象是在subscribe()方法中进行了赋值。for循环CopyOnWriteArrayList集合,并调用postToSubscription()

线程模型

等执行到postToSubscription()方法时,线程模型才派上了用场。

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

}

第一个分支:线程模型是POSTING,直接调用了invokeSubscriber()方法。

void invokeSubscriber(Subscription subscription, Object event) {

try {

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

} catch (InvocationTargetException e) {

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

} catch (IllegalAccessException e) {

throw new IllegalStateException(“Unexpected exception”, e);

}

}

很明显的看到,这是基于反射去调用方法,invoke方法接收两个参数,第一个参数是注册的对象,第二个参数是事件的Event。

从这里就可以看出来,POST并没有去做线程的调度什么的,事件处理函数的线程跟发布事件的线程在同一个线程。

第二个分支:线程模型是MAIN 首先判断了下事件发布的线程是不是主线程,如果是,执行invokeSubscriber()方法,invokeSubscriber()上面已经分析过,如果不是主线程,执行mainThreadPoster.enqueue(subscription, event)mainThreadPoster是继承自Handler,从这里大概可以猜到,这一步是去做线程调度的。/font>

我们看一看mainThreadPosterenqueue做了什么事:

void enqueue(Subscription subscription, Object event) {

// 封装了一个PendIngPost

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

synchronized (this) {

// 将PendIngPost压入队列

queue.enqueue(pendingPost);

if (!handlerActive) {

handlerActive = true;

// 调用了sendMessage()

if (!sendMessage(obtainMessage())) {

throw new EventBusException(“Could not send handler message”);

}

}

}

}

enqueue() 主要封装了一个PendingPost类,并把subscriptionevent作为参数传进去,紧接着把PendingPost压入到队列中去,然后sendMessage发了一条消息。

熟悉Handler机制的同学知道,处理消息是在handleMessage()方法中完成的:

public void handleMessage(Message msg) {

boolean rescheduled = false;

try {

long started = SystemClock.uptimeMillis();

while (true) {

PendingPost pendingPost = queue.poll();

if (pendingPost == null) {

synchronized (this) {

// Check again, this time in synchronized

pendingPost = queue.poll();

if (pendingPost == null) {

handlerActive = false;

return;

}

}

}

eventBus.invokeSubscriber(pendingPost);

long timeInMethod = SystemClock.uptimeMillis() - started;

if (timeInMethod >= maxMillisInsideHandleMessage) {

if (!sendMessage(obtainMessage())) {

throw new EventBusException(“Could not send handler message”);

}

rescheduled = true;

return;

}

}

} finally {

handlerActive = rescheduled;

}

}

代码有点多,我们主要看一下,它接收到消息后,是做了什么处理。从队列中取了消息,并且调用了eventBus.invokeSubscriber(pendingPost)方法,回到EventBus类中。

void invokeSubscriber(PendingPost pendingPost) {

Object event = pendingPost.event;

Subscription subscription = pendingPost.subscription;

PendingPost.releasePendingPost(pendingPost);

if (subscription.active) {

invokeSubscriber(subscription, event);

}

}

该方法内部还是去调用了invokeSubscriber()方法。

分析完线程模型为MAIN 的工作流程,不难做出结论,当发布事件所在的线程是在主线程时,我们不需要做线程调度,直接调用反射方法去执行。如果发布事件所在的线程不是在主线程,需要使用Handler做线程的调度,并最终调用反射方法去执行

第三个分支:线程模型是BACKGROUND。如果事件发布的线程是在主线程,执行backgroundPoster.enqueue(subscription, event),否则执行invokeSubscriber()

public void enqueue(Subscription subscription, Object event) {

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

synchronized (this) {

queue.enqueue(pendingPost);

if (!executorRunning) {

executorRunning = true;

eventBus.getExecutorService().execute(this);

}

}

}

PendingPost对象压入队列,然后调用eventBus.getExecutorService().execute(this),交给线程池去进行处理,它的处理是在Runnable的run()中。backgroundPoster实现了Runable接口:

@Override

public void run() {

try {

try {

while (true) {

PendingPost pendingPost = queue.poll(1000);

if (pendingPost == null) {

synchronized (this) {

// Check again, this time in synchronized

pendingPost = queue.poll();

if (pendingPost == null) {

executorRunning = false;

return;

}

}

}

eventBus.invokeSubscriber(pendingPost);

}

} catch (InterruptedException e) {

Log.w(“Event”, Thread.currentThread().getName() + " was interruppted", e);

}

} finally {

executorRunning = false;

}

}

最重要的还是eventBus.invokeSubscriber(pendingPost)这行代码,上面已经分析过。

第四个分支:线程模型是ASYNC。直接调用 asyncPoster.enqueue(subscription, event)asyncPoster也是实现了Runnable接口,里面也是使用的线程池,具体的操作就不分析了,感兴趣的可以去看一下源码,跟上一步操作类似。

(三) 高级用法

EventBus3.0较之前的版本有了一次改造,在3.0之后增加了注解处理器,在程序的编译时候,就可以根据注解生成相对应的代码,相对于之前的直接通过运行时反射,大大提高了程序的运行效率,但是在3.0默认的还是通过反射去查找用@Subscribe标注的方法,一般在使用的时候基本都是这个模式。 那我们怎么配置让EventBus使用注解器生成的代码呢? EventBus官网apt介绍

在这里我们重点提一下 EventBusBuilder类的:

boolean ignoreGeneratedIndex;

List subscriberInfoIndexes;

subscriberInfoIndexes变量可以去使用注解处理器生成的代码。SubscriberInfoIndex 就是一个接口,而注解生成器生成的类也是继承的它,我们也可以自己去继承它,定制自己的需求,不需要反射的EventBus。

我们再回过头来看一下注册过程的findUsingInfo()方法:

private List findUsingInfo(Class<?> subscriberClass) {

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 {

findUsingReflectionInSingleClass(findState);

}

findState.moveToSuperclass();

}

return getMethodsAndRelease(findState);

}

我们在前面分析的时候,直接分析的 findUsingReflectionInSingleClass(findState)方法,因为getSubscriberInfo()返回null,那什么时候getSubscriberInfo()返回不为null呢 ? 我们具体看看getSubscriberInfo()

最后

做任何事情都要用心,要非常关注细节。看起来不起眼的、繁琐的工作做透了会有意想不到的价值。
当然要想成为一个技术大牛也需要一定的思想格局,思想决定未来你要往哪个方向去走, 建议多看一些人生规划方面的书籍,多学习名人的思想格局,未来你的路会走的更远。

更多的技术点思维导图我已经做了一个整理,涵盖了当下互联网最流行99%的技术点,在这里我将这份导图分享出来,以及为金九银十准备的一整套面试体系,上到集合,下到分布式微服务

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

else {

findUsingReflectionInSingleClass(findState);

}

findState.moveToSuperclass();

}

return getMethodsAndRelease(findState);

}

我们在前面分析的时候,直接分析的 findUsingReflectionInSingleClass(findState)方法,因为getSubscriberInfo()返回null,那什么时候getSubscriberInfo()返回不为null呢 ? 我们具体看看getSubscriberInfo()

最后

做任何事情都要用心,要非常关注细节。看起来不起眼的、繁琐的工作做透了会有意想不到的价值。
当然要想成为一个技术大牛也需要一定的思想格局,思想决定未来你要往哪个方向去走, 建议多看一些人生规划方面的书籍,多学习名人的思想格局,未来你的路会走的更远。

更多的技术点思维导图我已经做了一个整理,涵盖了当下互联网最流行99%的技术点,在这里我将这份导图分享出来,以及为金九银十准备的一整套面试体系,上到集合,下到分布式微服务

[外链图片转存中…(img-iimEE9vp-1713600151335)]

[外链图片转存中…(img-1MuIasTk-1713600151335)]

[外链图片转存中…(img-p3rI7fMP-1713600151336)]

[外链图片转存中…(img-ev8XX548-1713600151337)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-g8cV8Akw-1713600151337)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值