EventBus原理解析
一、概述
EventBus我们都知道是用来做app内部的数据通信的,可以达到很好的解耦合效果,传统的的广播或则回调方式存在着代码耦合度高,代码杂乱,不宜维护的特点,EventBus使用的是订阅者/发布者模式,代码简洁,高度解耦,这个是这个框架产品的背景原因了
二、整体架构
这个是官方框架的图,这个图描述的结构很清晰
Subscriber:这个是订阅者,订阅某个类型的消息比如onMessage(String src) 这个就订阅了接受src的消息,当然代码中还要写入@Subscrber注解
Publisher:这个是发布消息方,发布的消息经过EventBus处理后,回调执行订阅方的方法
三、使用步骤
1、注册与反注册
EventBus.getDefault().register(this);
//注册的方式很简单,这个代码写在需要订阅的类里面,比如Activity就可以
EventBus.getDefault().unregister(this);
//这个是反注册,这个要写在类的销毁方法内,这个和register是成对出现的
2、订阅方法
@Subscribe(threadMode = ThreadMode.MAIN)
//这个是订阅模式,这个方法会在android的UI线程执行
//每个订阅方法只能有一个参数,发消息的时候会根据参数类型查找对应的方法
public void onEventBusMessage(BMessage msg)
{
Log.i("eventbus",Thread.currentThread().getId()+":"+msg.toString());
}
@Subscribe(threadMode = ThreadMode.ASYNC)
//这个方法会在android的子线程执行
public void onEventBusMessageSubThread(String msg)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.i("eventbus", "isCurrentThread:"+Looper.getMainLooper().isCurrentThread());
Log.i("eventbus", "mainthread.getId:"+Looper.getMainLooper().getThread().getId());
}
Log.i("eventbus",Thread.currentThread().getId()+":"+msg);
}
3、发送消息
//发送了一个String 类型的消息
EventBus.getDefault().post("subthread");
GameTestActivity.BMessage message=new GameTestActivity.BMessage();
message.code=2;
message.msg="ss";
//发送了一个BMessage 类型的消息
EventBus.getDefault().post(message);
4、执行结果
我们通过日志可以看到,准确的执行了
四、原理分析
1、整体执行流程
1、注册
将注册的那个目标类里面的订阅方法通过反射的方式提取出来,保存在EventBus中
2、发送消息
根据发送的消息的参数类型,在EventBus中查找对应的订阅者信息,如果找到了就执行(invoke)对应的方法
整体的原理还是比较简单的
2、详解过程
1、注册
1、
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
//上面这个就是查找出订阅了的方法信息,然后将每个方法信息 执行订阅过程
//SubscriberMethodFinder 这个类是用来查找注册方法的类
//我们接下来看下findSubscriberMethods 做了什么
2、
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//先调用缓存里的数据 如果有就直接返回结果
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注解索引的方式注册方法,这个是默认是false
if (ignoreGeneratedIndex) {
//通过反射的方式查找
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//apt方式
//这个方式是通过注解索引处理器的方式来注册方法,EventBus annotation processor ,
//这个是在编译期间完成的,速度要比传统的反射方式快很多,
//这个还需要EventBusBuild中做配置,还有配置EventBus的annotation processor
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;
}
}
我们在看下findUsingInfo这个方法,这个是注解处理器的方式实现
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//FindState的准备工作,里面会用到了复用机制有个pool
FindState findState = prepareFindState();
//设置findState 里面的subscriberClass 还有class 还有skipSuperClasses=false subscriberInfo=null 信息的初始化
findState.initForSubscriber(subscriberClass);
//下面这个while 是通过循环的方式 在自己的class以及父类的class中查找对应的注册信息
//while 最后面有findState.moveToSuperclass() 相当于把指针移动到了父类,直到没有为止
//当然这个可以通过Builder 中的skipSuperClasses = true 屏蔽掉
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
//subscriberInfo 这个是要配合注解处理的方式来做,默认是null
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);
}
findUsingReflectionInSingleClass(findState);
这个是实际的提取处理方法,继续看
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
//通过反射获取自身的methods 数组
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;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
//满足条件:1、是PUBLIC修饰符 2、但是不能有下面的修饰符
//Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC 中的一种
//Modifier 里面都是通过十六进制 并且相邻为1的方式声明数值,方便位运算
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取方法的参数,
Class<?>[] parameterTypes = method.getParameterTypes();
//只有一个参数的情况下才可以,所以EventBus 不支持多参数的方法注册
if (parameterTypes.length == 1) {
//获取Subscribe.class 的注解,这个做了判断 过滤出@Subscribe 的注解方法
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class );
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//获取线程模式,这个也是注解中写的
ThreadMode threadMode = subscribeAnnotation.threadMode();
//设置:优先级 priority 粘性事件sticky(post 之后注册也可以收到,通过缓存实现) //将new的SubscriberMethod 添加到findState的集合里面
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");
}
}
}
里面有个FindState 处理反射方式 和apt方式的中间类,处理相同逻辑的类,里面包含个各种集合数据
到此为止 我们已经获得List 结果集
然后遍历这个集合
然后我们继续看下EventBus的subscribe方法
//这个方法主要是保存数据还有,供后面的查询使用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//new 一个Subscription 这个是最后的存储的结果类
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType 这个是EventBus 存储注册方法的的集合
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
//添加一个空的CopyOnWriteArrayList
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
//添加一个newSubscription
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//储存eventType 的Map集合
subscribedEvents.add(eventType);
//如果这个是粘性事件 就会到stickyEvents 这里去查找然后查找 然后执行注册方法
//这个可以实现 先发消息 后注册也可以
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();
//最终会invoke到注册方法
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
上面的整个注册的逻辑就走完了,总结来说就是 查找注册了的方法 然后保存起来
2、发送消息
1、post(object)
public void post(Object event) {
//currentPostingThreadState 保存这当前的处理状态
//currentPostingThreadState 这个是ThreadLocal类型,
//这个可以保证每个线程都有各自的状态 不会冲突
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
//没有在发送的时候才可以发
if (!postingState.isPosting) {
//设置状态 当前是否在主线程操作
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
//正在发消息
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;
}
}
}
那么接下来我们就看下postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
//这个注册方法是否 查找到的 标志
boolean subscriptionFound = false;
//是否会从他们的父类查找对应的注册方法 默认是true
if (eventInheritance) {
//这个方法就是查找 包括父类的方法和接口, 对应类型的method信息
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
// |= 或等 只要有一个查找到有 最终结果就是有
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
//如果配置了NoSubscriberEvent 没发现注册方法 就发送NoSubscriberEvent事件
post(new NoSubscriberEvent(this, event));
}
}
}
我们接下来继续看postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根据对应的参数类型 在map中查找对应的结果value,这个结果是之前注册保存下来的
//这个是个数组,CopyOnWriteArrayList 这个类的特点是并发的时候 读性能很高,不担心同步问题
//因为读不会引起并发问题
subscriptions = subscriptionsByEventType.get(eventClass);
}
//下面就是便利所有注册的信息,然后执行
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//跳转到执行方法,下面继续分析
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
//这个方法是根据threadModel 线程模式来不同的执行
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
//这个isMainThread 代表发送消息是不是在主线程,如果是就直接执行就可以了,
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
//如果不是 就mainThreadPoster执行
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);
}
}
主要有2个重要的类
1、HandlerPoster
切换到主线程的HandlerPoster,这个其实是个Handler,初始化的时候在EventBus中 参数是Looper.getMainLooper()的,可以用来把子线程的操作 发送到 在主线程执行
这个类里面还有个队列,达到按照先进先处理的顺序 完成
如下:
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
//发送空消息 出发handleMessage 然后从队列里拿消息数据
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;
}
}
}
//最终会执行注册的方法,现在已经是ui线程了
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;
}
}
}
2、AsyncPoster
里面有异步的队列 执行处理,最终是调用了线程池getExecutorService().execute(this) 执行线程的
最后在run方法中执行了invokeSubscriber方法
class AsyncPoster implements Runnable {
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);
}
}
那么我们看下eventBus.invokeSubscriber,这个就是最后的执行反射执行注册方法的地方了
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);
}
}
五、总结
EventBus的整体逻辑并不复杂,逻辑很清晰
1、注册,提取注册方法 存储
2、发消息,查找对应的注册方法,然后invoke执行
在研究别人优秀框架的同时学习了优秀的设计和技术,拓宽了自己的思维,在android技术开发遇到瓶颈的时候,多学习好的框架和源码是非常好的手段和策略
水平有限,如有错误,敬请指教