在我们开发过程中,相信应该有很多人使用过EventBus 3.0
,这个确实方便了我们,少些了很多代码,这是个优秀的库,我们接下来进行对他剖析。
我们使用EventBus 3.0
的过程:
EventBus.getDefault().register()
EventBus.getDefault().post()
EventBus.getDefault().unregister()
我们先看看是怎么初始化的
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
this(DEFAULT_BUILDER);
}
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;
}
一个单例模式,接着点你会发现其实是利用的Builder
模式,而框架内部帮我们写了一个EventBusBuilder
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
EventBusBuilder() {
}
在EventBusBuilder
初始化的时候除了对成员变量的一些初始化外,其他的并没有做什么操作。
接下来我们进入register
函数
/**
* 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();
//根据subscriberClass 获取订阅的方法
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历订阅
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
从这个函数我们可以很清晰的看到逻辑,首先根据subscriber(Activity,Fragment)
得到相应的订阅方法,然后在遍历订阅。这里代码就不点进去看了,主要说下框架的实现思路。
采用了缓存加反射的方式,主要的参数和类为下:
METHOD_CACHE
:一个key
为订阅者class
,value
为需要订阅实现的方法的一个ConcurrentHashMap
,这个HashMap
是为了保证线程并发安全。我们获取的订阅方法就会缓存到这里面。
FindState
:顾名思义,这是一个检测状态的类,里面的参数为
final List subscriberMethods = new ArrayList<>();
final Map anyMethodByEventType = new HashMap<>();
final Map subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class> subscriberClass;
Class> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
里面比较重要的几个参数:
SubscriberMethod
: 当前订阅对象(Activity,Fragment)
的订阅方法实体。
public class SubscriberMethod {
//方法
final Method method;
//模式
final ThreadMode threadMode;
//类型, 就是参数的类型 参数只能为1个
final Class> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
subscriberMethods
:用来存储当前订阅对象(Activity,Fragment)
内的订阅方法SubscriberMethod
。
anyMethodByEventType
: key
为参数类型(订阅类型),value
为方法的一个缓存。
subscriberClassByMethodKey
:key
为 订阅方法名+">"+参数类型name
,value
为订阅方法的class
。
获取订阅方法的时候先判断缓存里面是否存在,不存在就获取FindState
实例,根据反射获取注解Subscribe(Activity,Fragment)
的方法,然后对FindState
实例进行操作(anyMethodByEventType
和subscriberClassByMethodKey
得到相应的值或添加到缓存),获取到的订阅方法集合其实就是FindState
实例里面的subscriberMethods
,最后在把获取的订阅方法集合加入缓存。
得到了订阅方法集合接下来就遍历subscriberMethods
:
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map
订阅的时候,首先根据订阅者(Activity Fragment)
和SubscriberMethod
得到一个Subscription
对象,这个类就是一个真正执行信息传递的订阅对象。
/*
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.greenrobot.eventbus;
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
/**
* Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
*/
volatile boolean active;
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true;
}
@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
@Override
public int hashCode() {
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
}
}
接着把这个Subscription
对象添加CopyOnWriteArrayList
(CopyOnWriteArrayList
是一个适合用在读多,写少
的并发
应用中,它是一个线程安全的集合)然后将这个CopyOnWriteArrayList
添加到subscriptionsByEventType
里面,这个subscriptionsByEventType
是一个key
为订阅方法的类型(方法函数的类型),value
为一个存放Subscription
的CopyOnWriteArrayList
的HashMap
。接下来把订阅方法的类型(参数类型)eventType
添加到一个ArrayList
里面,在将这个ArrayList
添加到typesBySubscriber
,typesBySubscriber
是一个key
为订阅者对象(Fragment,Activity
),value
为ArrayList
的HashMap
,最后在判断是否是sticky
,如果是的就将遍历一个stickyEvents
的HashMap
,然后根据key
(订阅方法类型)发出消息。
我们看看unregister
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
List subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
很简单,先根据订阅者(Activity Fragment)
获取到typesBySubscriber
里面的订阅方法类型集合(List
),然后遍历这个集合,根据这个订阅方法类型得到subscriptionsByEventType
里面的订阅对象集合List
,在遍历这个subscriptions
集合,判断subscription.subscriber == subscriber
然后移除,最后在移除typesBySubscriber
里面的这个订阅对象(Fragment ,Activity )
。
接下来我们看看post
,最终会走到这个函数
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);
}
}
/**
* 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);
}
}
在发送消息的时候根据消息类型(参数类型)
然后从上面的subscriptionsByEventType
里面取出相应的数据进行封装成一个PendingPost
对象,在根据反射invoke
对应的方法即可,如果是MAIN
的话就通过Handler
进行线程转换,如果是BACKGROUND
并且是在主线程中调用或者是ASYNC
将会通过线程池来进行线程切换。
接下来看看postSticky
:
/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
这个其实我们在注册的时候已经分析了,当调用这个函数的时候,将会将这个消息加到stickyEvents
里面,这个stickyEvents
是一个ConcurrentHashMap
,ConcurrentHashMap
是一个应用于高并发的键值对,接着调用post
函数先把已经注册的观察者的方法实现,接下来其他对象注册的时候
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, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
}
在这里将会调用。
还有一个优先级priority
问题,可以看到也是在注册的时候
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;
}
}
其实也是很简单的原理。源码分析得也差不多了,具体的需要大家自己去查看源码。
总结,EventBus
的实现方式:反射 + 数据封装 + 缓存 + 线程切换
通过查看EventBus
的源码收获了什么?
Builder
模式封装参数
- 根据用处不同进行对不同的类封装,比如:
SubscriberMethodFinder
用于订阅方法的查找;FindState
一个用SubscriberMethodFinder
查找的辅助类,里面封装了一些数据;SubscriberMethod
订阅方法实体,就是通过注解的方法对应的对象;Subscription
一个订阅对象。 - 缓存
- 线程切换使用方式
- 反射用法熟悉加强
-
ThreadLocal
是一个关于创建线程局部变量的类。
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal
创建的变量只能被当前线程访问,其他线程则无法访问和修改。 -
CopyOnWriteArrayList
:是一个适合用在读多,写少的并发应用中,它是一个线程安全的集合 -
ConcurrentHashMap
:是一个应用于高并发的键值对
人生苦短,要学会享受!但是我还没到应该安于享受的年龄!