EventBus的源码详解与架构分析,使用EventBus会造成什么弊端,带你复现整个思路历程(1)

/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
// 这一步不存在线程问题,volatile保证了。如果没有defaultInstance实例化出来,
if (defaultInstance == null) {
synchronized (EventBus.class) {
// 进入同步块的时候,不能保证defaultInstance没有被实例化出来,所以需要进行double-check
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}

// 这里实现的时候,考虑的是defaultInstance 不一定是每个人都需要创建的,否则没必要使用lazy的实现方式
// 下面是一种实现方式
static {
defaultInstance = new 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

最后

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

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

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

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值