基本使用
引入
在模块gradle
脚本中引入EventBus
implementation 'org.greenrobot:eventbus:3.2.0'
定义事件
public static class MessageEvent { /* Additional fields if needed */ }
订阅事件
通过Subscribe
订阅事件.注解中包含许多参数,如线程环境,事件优先级,以及是否为粘性事件.
/**
* 订阅事件
* 运行在UI线程中
* 优先级为1
* 非粘性事件
*/
@Subscribe(threadMode = ThreadMode.MAIN,priority=1,sticky=false)
public void onMessageEvent(MessageEvent event) {/* Do something */};
注册与反注册
在需要观察事件的组建中注册,同时在组建销毁时候进行反注册
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
发送事件
EventBus.getDefault().post(new MessageEvent());
源码分析
EventBus实例
EventBus
通过静态方法EventBus.getDefault()
获取实例.
// 单例模式
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
看其构造方法,它采用了构造器模式
,并且内部存在一个静态的构造器builder
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
查看其构造器
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
// 发布的事件是否支持继承,即当发布子事件时候,其订阅其父类的订阅者也会收到通知
boolean eventInheritance = true;
// 是否强制使用反射
boolean ignoreGeneratedIndex;
// 严格的方法验证
boolean strictMethodVerification;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
Logger logger;
MainThreadSupport mainThreadSupport;
EventBusBuilder() {
}
}
EventBusBuilder
中的几个属性必须值得注意
eventInheritance
定义的事件是否支持继承(多层次模式).该参数默认是支持的.支持则意味着当发布子事件时候,其父事件的订阅者也会接到通知.但是,这回影响到事件发布越接收的速度.ignoreGeneratedIndex
在3.0
的版本后,EventBus
加入了编译时注解解析器去解析订阅的方法响应,以替代运行时注解从而提高运行效率.该参数代表是否强制使用运行时反射.strictMethodVerification
严格的方法验证.
以上三点非常重要
订阅方法的解析
EventBus
方法的注册方法如下
public void register(Object subscriber) {
// 注册者的类型
Class<?> subscriberClass = subscriber.getClass();
// 订阅方法的解析
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 订阅
subscribe(subscriber, subscriberMethod);
}
}
}
register
方法传入的方法参数为Object
.获取该对象的type
,然后通过SubscriberMethodFinder
类的findSubscriberMethods
获取所有的订阅方法.订阅方法的抽象类为SubscriberMethod
.遵循单一性原则
,SubscriberMethodFinder
的唯一功能就是解析订阅信息.
public class SubscriberMethod {
// 方法
final Method method;
// 线程模型
final ThreadMode threadMode;
// 事件类型,
final Class<?> eventType;
// 优先级
final int priority;
// 是否是粘性事件
final boolean sticky;
// 可以唯一标识这个对象
String methodString;
}
需要注意的是findSubscriberMethods
返回的是List
对象,说明一个组建中可以订阅多种类型的事件.查看订阅的具体逻辑
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 优先从缓存中拿订阅的方法
List<SubscriberMethod> 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;
}
}
通过反射方法获取订阅方法
强制通过反射方法获取订阅方法
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
// FindState可以理解为寻找订阅方法的状态容器.
// 因为FindState内部存放了大量的数据,性能开销比较大,所以这里巧妙的使用了数据池优化
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 这里是通过`class`不为空来实现递归的解析类结构层次
while (findState.clazz != null) {
// 反射获取注解方法
findUsingReflectionInSingleClass(findState);
// 将解析动作转移到父类
findState.moveToSuperclass();
}
// 回收数据到数据池
return getMethodsAndRelease(findState);
}
FindState
可以理解为订阅信息的一种临时性容器,其内部存放了大量的零时性数据,其性能开销比较大.但出于业务需求,又需要经常使用这种容器,为了避免频繁的创建和销毁引起的性能浪费(内存抖动),这里使用了数据池模式优化.
// 数据池中第一个非空数据,如果数据池中没有数据,则创建一个
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
在从数据池中取得数据后,为了防止脏数据,需要重新初始化
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
在完成数据的解析与查找后,都会调用getMethodsAndRelease
方法
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> 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;
}
在上述的方法中可以看到使用的FindState
对象调用recycle
方法释放内部数据,同时将数据放回数据池中,从而达到数据重用的效果.这是整个数据池模式过程.
讲完数据池模式,再查看具体的订阅信息的采集.该部分的逻辑集中在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
try {
// 获取类中的所有方法,包含继承的方法
methods = findState.clazz.getMethods();
} catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
String msg = "Could not inspect methods of " + findState.clazz.getName();
if (ignoreGeneratedIndex) {
msg += ". Please consider using EventBus annotation processor to avoid reflection.";
} else {
msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
}
throw new EventBusException(msg, error);
}
// getDeclaredMethods跳过了父类的方法
findState.skipSuperClasses = true;
}
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的注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
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)) {
// 订阅方法只能有一个订阅参数u
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");
}
}
}
通过反射的方式解析订阅信息算是比较简单,通过字节码文件Class
对象,获取本类的定义的所有方法,再通过一连串的过滤条件,确认是符合条件的订阅方法后,将订阅信息汇总后加入FindState
当中.具体的过滤条件有
- 订阅方法必须是
public
的 - 订阅方法必须是非静态(
static
),非抽象(abstract
),非合成,非桥接(后续两种类型都是编辑期间添加的) - 订阅方法必须含有
Subscribe
的方法注解 - 订阅方法必须只含有一个参数
需要需要注意的一个细节是,订阅方法的解析是有层级的,他总是先解析本类中的订阅方法,在上述的源码中,它是优先通过findState.clazz.getDeclaredMethods
方法获取本类当中定义的方法,并没有获得从父类当中继承的方法,并且通过findState.skipSuperClasses = true
记录了这种状态.在解析完本类中的方法后,他会继续通过moveToSuperclass
方法以迭代的方式去解析上一级父类中的订阅方法
void moveToSuperclass() {
// 这里的参数是EventBuilder中设置的
if (skipSuperClasses) {
clazz = null;
} else {
// 获取父类
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
// Skip system classes, this degrades performance.
// Also we might avoid some ClassNotFoundException (see FAQ for background).
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||
clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
clazz = null;
}
}
}
如果用户设置了skipSuperClasses
则不会继续去解析上级父类,否则会继续解析下去.在findUsingReflection
方法中是通过一个while
循环来判断findState.class == null
来循环解析类结构层级的.
至此,通过反射方式解析订阅方法流程到此结束.
通过注入的信息获取订阅方法
除了通过反射的方式获取订阅方法,EventBus3.0
还通过注解处理器实现编译期类型注入,从而极大的提高了一直被别人诟病的性能问题.这部分的代码从findUsingInfo
开始
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// 从数据池中获取状态容器
FindState findState = prepareFindState();
// 重新初始化
findState.initForSubscriber(subscriberClass);
// 通过判断findState.class是否为空,从而达到类多层级的解析,
// 后续通过findState.moveToSuperclass方法,将解析动作向父类推进
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.subscribeInfo字段中的.
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
// 这里subscriberInfoIndexes是用于存放所有注解解析的索引信息的.
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
这里知道,编译时注解解析器解析注解生成的是SubscriberInfo
,这是一个接口
public interface SubscriberInfo {
// 注解所在的类
Class<?> getSubscriberClass();
// 注解的方法
SubscriberMethod[] getSubscriberMethods();
// 注解的父类的解析结果
SubscriberInfo getSuperSubscriberInfo();
// 是否解析了父类
boolean shouldCheckSuperclass();
}
解析器解析的所有结果会被最终放入到SubscriberMethodFinder
类中的subscriberInfoIndexes
容器中,追踪该数据的来源,它是通过构造方法注入的
SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
boolean ignoreGeneratedIndex) {
// 通过构造函数注入了
this.subscriberInfoIndexes = subscriberInfoIndexes;
this.strictMethodVerification = strictMethodVerification;
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}
而SubsriberMethodFinder
是在EventBus
类中完成构建的
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
// subsribeInfoIndex 来源于其builder中
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;
}
而EventBus
是使用构建者模式构建的,他实例化使用了EventBusBuilder
类中的subscriberInfoIndexes
,查看EventBusBuilder
源码,发现最终通过如下的方法向其添加订阅信息索引的
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
查看EventBusAnnotationProcessor
源码,其中确实有通过该方法注入订阅信息
private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {
for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
if (classesToSkip.contains(subscriberTypeElement)) {
continue;
}
String subscriberClass = getClassString(subscriberTypeElement, myPackage);
if (isVisible(myPackage, subscriberTypeElement)) {
writeLine(writer, 2,
"putIndex(new SimpleSubscriberInfo(" + subscriberClass + ".class,",
"true,", "new SubscriberMethodInfo[] {");
List<ExecutableElement> methods = methodsByClass.get(subscriberTypeElement);
writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage);
writer.write(" }));\n\n");
} else {
writer.write(" // Subscriber not visible to index: " + subscriberClass + "\n");
}
}
}
至于如何通过注解处理器解析注解,在此不做过多深究.
订阅
在获取到订阅方法后,就要实行真正的订阅动作了
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 订阅的事件类型
Class<?> eventType = subscriberMethod.eventType;
// 将订阅的事件类型与订阅者组成Subscription实例
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType会记录没一个事件的所有订阅信息
CopyOnWriteArrayList<Subscription> 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的优先级插入到数组中,按照优先级高的排序
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;
}
}
// 这里记录没一个suscriber的类型
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<Class>).
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);
}
}
}
在订阅过程中,解析的订阅方法与订阅者一起组成了Subscription
对象.这个类的实例对象是EventBus
各种操作的基本单位,如订阅,解除订阅,发送信息等.随后,新注册的订阅者会被依据不同的订阅消息类型放在同一个容器内,并且在该容器内以优先级参数priority
.同时记录订阅者的类型.在完成这些动作后,EventBus
需要确定订阅者订阅的消息类型是否是粘性消息
,如果是,则需要将最新存储的粘性消息发送给订阅者.
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, isMainThread());
}
}
至此,整个订阅动作完成.
如何实现线程间转换
在postToSubscription
中会将事件发送订阅者执行订阅方法,如果没猜错的话就是将解析的订阅方法继续通过反射invoke
.但是订阅方法是可以定义线程模式的.EventBus
是如何实现线程间切换的呢.先看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 MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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);
}
}
MAIN
这种线程模型应该要求执行环境在UI线程
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
如果当前线程是在UI线程
中,直接调用订阅方法,否则通过mainThreadPoster
处理.mainThreadPoster
是一个Poster
.这是一个接口
interface Poster {
/**
* Enqueue an event to be posted for a particular subscription.
*
* @param subscription Subscription which will receive the event.
* @param event Event that will be posted to subscribers.
*/
void enqueue(Subscription subscription, Object event);
}
它主要为要发布的事件实现排列等待的功能.
在EventBus
中,它的实例化代码如下
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
它是由MainThreadSupport
实例创建的.
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
// 静态内部实现类
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
// 在其构造函数中需要传如一个Looper实例
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
// 这里知道他返回的是HandlerPost对象
return new HandlerPoster(eventBus, looper, 10);
}
}
}
在EventBus
的构建者类中,部分的源码如下
// 这里获取MainThreadSupport
MainThreadSupport getMainThreadSupport() {
if (mainThreadSupport != null) {
return mainThreadSupport;
} else if (AndroidLogger.isAndroidLogAvailable()) {
Object looperOrNull = getAndroidMainLooperOrNull();
return looperOrNull == null ? null :
new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
} else {
return null;
}
}
static Object getAndroidMainLooperOrNull() {
try {
return Looper.getMainLooper();
} catch (RuntimeException e) {
// Not really a functional Android (e.g. "Stub!" maven dependencies)
return null;
}
}
一眼就可以看出切换UI线程
的方式是通过Handler
.通过Looper.getMainLooper
方法获取主线程的Looper
,创建HandlerPoster
对象.
/*
* 1. 这里PendingPostQueue充当的是`MessageQueue`的角色,两者的功能非常相近
*
*/
public class HandlerPoster extends Handler implements Poster {
// 自定义的消息队列 MessageQueue
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
// 这里应该也用了数据池优化
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
// 入队是同步的动作
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
// 每一次的入队都通过sendMessage唤醒消息的执行
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
// 接受到消息后,就会进行消息循环;
@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;
}
}
}
HandlerPoster
继承Handler
.这里需要注意的一些细节是在HandlerPoster
内部,自己实现了一个PendingPostQueue
.它为什么要这样做?其实,道理很简单.第一,它实现的Poster
接口要求它必须实现给消息排序的功能(注册方法有可能存在耗时业务),第二,则是原生的MassageQueue
装载的对象是Message
,而Message
对象已经无法装载数据了.所以,此时只能自己去实现,但是,但是,其内部的功能与原来与原生MessageQueue
是一样的.所以,此部分的代码与Handler机制
部分源码的功能非常接近.
BACKGROUND
这种线程模型应该要求执行环境在后台线程
.
直接查看其Poster
final class BackgroundPoster implements Runnable, Poster {
// 他的顺序也是通过`PendingPostQueue`来实现的
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
// 如对
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
// 这里使用了EventBus线程池
eventBus.getExecutorService().execute(this);
}
}
}
@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) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
这里没有使用Handler
技术.BackgroundPoster
本身是一个Runnable
,在其run
方法中不断去轮循队列中的Subscription
,只要存在未执行的的Subscription
就会去执行.它的执行环境是EventBus
线程池中的线程.需要注意的时,它的执行步骤都是同步的,所以,意味着,定义的订阅函数最好不要有太多耗时的业务,避免事件的发送延迟.
ASYNC
这种线程模型应该要求执行环境在后台线程
,并且是异步的.
class AsyncPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
这里的代码跟BACKGROUND
中唯一的不同就是没有加入同步的操作,这意味着,下个消息的执行不依赖上个消息的执行状态,只要有消息,并且线程池中有空闲的线程,订阅方法就会被调用.在这种情况下,订阅方法是可以执行耗时的动作的.
发送消息
最后看看如何发送消息
public void post(Object event) {
// EventBus会为每个发送事件的线程保存一个线程变量
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
// 如果没有没有在发送状态,则启动发送
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
// 将队列中存在的所有事件都发送出去
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
这里会为调用post
方法线程创建一个线程间变量,改变量用于保存发送数据.ThreadLocale
在Hanlder
机制中也出现过.在确定该线程队列中存在未post
的消息,则会通过postSingleEvent
方法逐个发送.后续的代码比较简单,不再继续分析
至此,EventBus
的大体流程分析完毕.