EventBus的源码详解与架构分析,使用EventBus会造成什么弊端,带你复现整个思路历程,Android工程师面试题及答案

本文详细解释了EventBus的EventBusBuilder的使用,重点分析了register方法中的事件订阅逻辑,包括findSubscriberMethods核心代码,展示了EventBus如何处理事件分发和线程模型。同时提及了EventBus在继承和线程转换方面的处理以及生产者消费者模式的应用。

}

EventBus实现了EventBusBuilder,通过Builder的方式使得构建的时候更加容易

public static EventBusBuilder builder() {
return new EventBusBuilder();
}

下面重点看看register(Object subscriber, boolean sticky, int priority)方法

private synchronized void register(Object subscriber, boolean sticky, int priority) {
// 用 subscriberMethodFinder 提供的方法,找到在 subscriber 这个类里面,订阅的内容。
List subscriberMethods
= subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 遍历这些方法,subscribe 这些事件
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}

findSubscriberMethods 这个方法是实现 EventBus 的核心代码,这里面包含了 EventBus 隐式定义的交互协议。从这个方法里面,可以看到如何争取地使用EventBus。

List findSubscriberMethods(Class<?> subscriberClass) { String key = subscriberClass.getName(); List subscriberMethods; // 如果这个 Class 对应的方法被缓存,直接返回。 synchronized (methodCache) { subscriberMethods = methodCache.get(key); } // 这个方法其实可以放在 前面的 synchronized 模块里面 if (subscriberMethods != null) { return subscriberMethods; } subscriberMethods = new ArrayList(); Class<?> clazz = subscriberClass;
HashSet eventTypesFound = new HashSet();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
// 跳过JDK里面的类
if (name.startsWith(“java.”) || name.startsWith(“javax.”) || name.startsWith(“android.”)) {
// Skip system classes, this just degrades performance
break;
}

// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// 获取所有声明的方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 方法是 Public 的
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;

// 方法的前缀是否是 ‘OnEvent’, 如果是‘OnEvent’,查看后面的字符串,这里定义了 4 种基本类型
// ThreadModel 会在后面介绍
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals(“MainThread”)) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals(“BackgroundThread”)) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals(“Async”)) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}

// 获取参数类型
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append(‘>’).append(eventType.getName());
// 得到类似于一个句柄的东西,比如 onEventMainThread>DownloadInfo
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + “.”

  • methodName);
    }
    }
    }
    // 这里要为 EventBus 点个赞了,EventBus 是支持继承的
    clazz = clazz.getSuperclass();
    }
    if (subscriberMethods.isEmpty()) {
    throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
  • ON_EVENT_METHOD_NAME);
    } else {
    synchronized (methodCache) {
    methodCache.put(key, subscriberMethods);
    }
    return subscriberMethods;
    }
    }

现在看下如何把 subscriberClass 里面的内容订阅到 EventBus 里面去。

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
// 获取订阅了某种类型数据的 Subscription 。 使用了 CopyOnWriteArrayList ,这个是线程安全的,
// CopyOnWriteArrayList 会在更新的时候,重新生成一份 copy,其他线程使用的是
// copy,不存在什么线程安全性的问题。
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
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);
    }
    }

// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true);

int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
// 根据优先级进行插入,其实这里可以替换为优先级队列的
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}

List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList

if (sticky) {
// 是否支持继承,这个可以在 Builder 的时候指定,如果不支持,那么可能有20%以上的性能提升
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

EventBus ThreadModel EventBus 一共提供了 4 种 ThreadModel

分别是

PostThread,

MainThread,

#####BackgroundThread,

Async

PostThread 默认实现,执行发生在同一个线程 MainThread 执行在UI 线程上 BackgroundThread 回调发生在非 UI 线程上 Async 永远执行在一个其他的线程上 以上这四种类型,足以支持观察者模式里面需要进行的异步处理。

#####EventBus 如何实现线程转换的

但凡经历一些实际项目,就会发现,经常存在「生产」和「消费」冲突的情况,这里就需要使用「生产者与消费者」模式。

EventBus 中 生产者和消费者模式的实现主要是在 PendingPostQueue里面。

PendingPostQueue 的实现比较简单,主要是通过在 enqueue 和 poll 的时候进行 synchronized 同步来实现的。

synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException(“null cannot be enqueued”);
}
// 将 Post 插入到队列尾部
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
// 在最开始的时候,建立头部和尾部的索引
head = tail = pendingPost;
} else {
throw new IllegalStateException(“Head present, but no tail”);
}
notifyAll();
}

synchronized PendingPost poll() {
PendingPost pendingPost = head;
// 从头部获取
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}

// 这里需要注意的地方是 PendingPost, 这里维护了一个 pendingPostPool 的池子, 当PendingPost 不再需要的时候,就释放回池子里面去,避免了新建对象的开销。
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don’t let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}

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

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

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

img

img

img

img

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

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

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

最后

Android学习是一条漫长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。

人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。

资源持续更新中,欢迎大家一起学习和探讨。

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

长的道路,我们要学习的东西不仅仅只有表面的 技术,还要深入底层,弄明白下面的 原理,只有这样,我们才能够提高自己的竞争力,在当今这个竞争激烈的世界里立足。

人生不可能一帆风顺,有高峰自然有低谷,要相信,那些打不倒我们的,终将使我们更强大,要做自己的摆渡人。

资源持续更新中,欢迎大家一起学习和探讨。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值