EventBus源码解析,2024最新Android大厂面试真题大全

本文详细解析了EventBus的单例模式构造过程,特别关注EventBusBuilder的作用,以及EventBus如何通过HashMap管理事件订阅、发布和线程调度。重点介绍了三种Poster(主线程、后台和异步)在事件分发中的角色以及SubscriberMethodFinder的注解处理方法。
摘要由CSDN通过智能技术生成

看到这里有没有疑惑的地方?既然是单例模式为什么构造方法不是private而是public,这是因为EventBus可能有多条总线,订阅者注册到不同线上的 EventBus,通过不同的实例来发送数据,不同的 EventBus 是相互隔离开的,订阅者都只会收到注册到该线上事件。

构造方法中只有一个DEFAULT_BUILDER,接着我们来看DEFAULT_BUILDER

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

我们可以看出EventBus是通过一个EventBusBuilder的实例来构建的

EventBus(EventBusBuilder builder) {

subscriptionsByEventType = new HashMap<>();

typesBySubscriber = new HashMap<>();

stickyEvents = new ConcurrentHashMap<>();

mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);

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;

}

subscriptionsByEventType = new HashMap<>();

typesBySubscriber = new HashMap<>();

stickyEvents = new ConcurrentHashMap<>();

前三行是三个hashMap但有所不同:

subscriptionsByEventType 对应的hashMap是:

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

它的key是订阅事件,value是所有订阅者集合。当我们发送Event事件的时候都是从这个集合中去寻找,

typesBySubscriber 对应的hashMap是:

private final Map<Object, List<Class<?>>> typesBySubscriber;

它的key是订阅者对象,value是这个订阅者订阅的所有事件集合。当我们注册或者反注册的时候都是操作这个集合然后操作subscriptionsByEventType。

stickyEvents 对应的hashMap是:

private final Map<Class<?>, Object> stickyEvents;

它的key是粘性事件的class对象,value是粘性事件对象。接着我们看后面三行代码:

mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); backgroundPoster = new BackgroundPoster(this);

asyncPoster = new AsyncPoster(this);

mainThreadPoster

我们知道,在此之前我们线程间通信或者传值的话 都是使用Hanlder,而mainThreadPoster 的本质就是一个Hanlder,我们点击mainThreadPoster 进去看

既然是hanlder所以我们主要来看它的hanleMessage方法

@Override

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;

}

}

从队列中不断的去处pendingPost,使用 eventBus.invokeSubscriber(pendingPost)进行事件分发,而pendingPost是一个可复用对象的复用池,通过obtainPendingPost方法进行复用,releasePendingPost方法进行回收,这个HandlerPoster是运行在主线程中的,因为初始化的时候有个 Looper.getMainLooper()。

pendingPost主要有三个对象:事件、订阅和一个节点

backgroundPoster

backgroundPoster本质是一个Runnable,主要在后台处理事件,所以我们主要看它的run方法

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;

}

}

mainThreadPoster是一样的,从复用池中取出,如果队列为空就间隔1秒再取,然后调用invokeSubscriber方法进行分发

asyncPoster

asyncPoster的本质也是一个Runnable

@Override

public void run() {

PendingPost pendingPost = queue.poll();

if(pendingPost == null) {

throw new IllegalStateException(“No pending post available”);

}

eventBus.invokeSubscriber(pendingPost);

}

但是每次只取一个pendingPost。不论发布线程是否为主线程,都使用一个空闲线程来处理。Async类的所有线程是相互独立的,因此不会出现卡线程的问题。

说到这里这三个poster都是负责线程调度的,最后都调用invokeSubscriber进行事件分发,那么我们有必要来来看下这个invokeSubscriber方法。

invokeSubscriber

/**

  • Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents race conditions

  • between {@link #unregister(Object)} and event delivery. Otherwise the event might be delivered after the

  • subscriber unregistered. This is particularly important for main thread delivery and registrations bound to the

  • live cycle of an Activity or Fragment.

*/

void invokeSubscriber(PendingPost pendingPost) {

Object event = pendingPost.event;

Subscription subscription = pendingPost.subscription;

PendingPost.releasePendingPost(pendingPost);

if (subscription.active) {

invokeSubscriber(subscription, event);

}

}

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

}

}

这样就比较一目了然了,从peningPost中得到事件和订阅,通过反射调用了订阅者的订阅函数并把event对象作为参数传入。

二、注册事件

======

/**

  • Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they

  • are no longer interested in receiving events.

  • 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 subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

synchronized (this) {

for (SubscriberMethod subscriberMethod : subscriberMethods) {

subscribe(subscriber, subscriberMethod);

}

}

}

我们可以看到这个注册方法

首先获取订阅者的class对象,通过subscriberMethodFinder寻找这个class对象的所有订阅方法集合SubscriberMethod,SubscriberMethod,而SubscriberMethod中包含了相应的线程、Method对象、事件类型、优先级记忆是否是粘性事件等。

然后通过 subscribe(subscriber, subscriberMethod);订阅事件,而subscriberMethod对象是通过subscriberMethodFinder获取的,所以我们先来看下subscriberMethodFinder的实现

subscriberMethodFinder

subscriberMethodFinder类是用来查找和缓存订阅者响应函数的信息的类,那么我们如何获取订阅者响应函数的信息,这里就要提到APT运行时注解了,当然我们这里不对APT进行介绍,EventBus中是通过@Subscribe()注解来获取的,我们来看下@Subscribe()

言归正传我们来看subscriberMethodFinder方法

List findSubscriberMethods(Class<?> subscriberClass) {

List subscriberMethods = METHOD_CACHE.get(subscriberClass);

if (subscriberMethods != null) {

return subscriberMethods;

}

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

} else {

METHOD_CACHE.put(subscriberClass, subscriberMethods);

return subscriberMethods;

}

}

从METHOD_CACHE取看是否有缓存,key是保存订阅类的类名,value是保存类中订阅的方法数据,如果忽略注解器生成的MyEventBusIndex类就调用反射来获取订阅类中的订阅方法信息,如果没有忽略就从注解生成的是否忽略注解器生成的MyEventBusIndex类中来获取订阅类中的订阅方法信息。

findUsingReflection

private List findUsingReflection(Class<?> subscriberClass) {

FindState findState = prepareFindState();

findState.initForSubscriber(subscriberClass);

while (findState.clazz != null) {

//通过反射来获得订阅方法信息

findUsingReflectionInSingleClass(findState);

//查找父类的订阅方法

findState.moveToSuperclass();

}

//返回订阅方法集合

return getMethodsAndRelease(findState);

}

findUsingReflectionInSingleClass

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;

}

//遍历Method

for (Method method : methods) {

int modifiers = method.getModifiers();

if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {

Class<?>[] parameterTypes = method.getParameterTypes();

//保证必须只有一个事件参数

if (parameterTypes.length == 1) {

//得到注解

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

if (subscribeAnnotation != null) {

Class<?> eventType = parameterTypes[0];

//校验是否添加该方法

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

ThreadMode threadMode = subscribeAnnotation.threadMode();

//实例化SubscriberMethod对象并添加

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

findUsingInfo是通过运行时注解生成的MyEventBusIndex类来获取订阅方法信息

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

}

getSubscriberInfo

private SubscriberInfo getSubscriberInfo(FindState findState) {

//判断FindState对象中是否有缓存的订阅方法

if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {

SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();

if (findState.clazz == superclassInfo.getSubscriberClass()) {

return superclassInfo;

}

}

//从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息

if (subscriberInfoIndexes != null) {

for (SubscriberInfoIndex index : subscriberInfoIndexes) {

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

if (info != null) {

return info;

}

}

}

return null;

}

这样,订阅类的所有SubscriberMethod都已经被保存了,最后再通过getMethodsAndRelease()返回List。

然后我们再回到注册中的subscribe(subscriber, subscriberMethod);方法,方法代码如下:

//必须在同步代码块里调用

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

//获取订阅的事件类型

Class<?> eventType = subscriberMethod.eventType;

//创建Subscription对象

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

//从subscriptionsByEventType里检查是否已经添加过该Subscription,如果添加过就抛出异常

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

}

}

//根据优先级priority来添加Subscription对象

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;

}

}

//将订阅者对象以及订阅的事件保存到typesBySubscriber里.

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

if (subscribedEvents == null) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

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

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。

不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?

我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。

我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:

部分目录截图

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Hj-1712487040458)]

[外链图片转存中…(img-5Z7ERjMu-1712487040459)]

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

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。

不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?

我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。

我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:

[外链图片转存中…(img-EJXHmAzB-1712487040459)]

[外链图片转存中…(img-yae4N8jg-1712487040459)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值