关于EventBus的介绍和使用,此处只做基本介绍,很多开发者都很熟悉其使用方法,也尝到了EventBus在开发中的便捷之处,关于EventBus的使用和源码笔者也是早有接处,本文主要是针对其源码进行分析,带你一起探索EventBus的代码细节
data class UserEvent(val id: Int, var name: String, var age: Int)
EventBus.getDefault().register(this)
EventBus.getDefault().unregister(this)
@Subscribe
public fun toast(userEvent: UserEvent) {
Toast.makeText(this, "${userEvent.id} ${userEvent.name} ${userEvent.age}", Toast.LENGTH_SHORT).show()
}
EventBus.getDefault().post(UserEvent(1,"AAAA",30))
EventBus.getDefault().postSticky(UserEvent(1,"AAAA",30))
对源码的分析还遵循着之前的以Arouter为例谈谈学习开源框架的最佳姿势中讲解的开源框架的学习姿势进行分析,主要从一下几个方面:
上面为EventBus源码的时许图,为了体现重要的细节画的有点乱,这里先根据时许图用通俗的话简单介绍下工作过程方便后面的理解,首先Client的注册EventBus时,EventBus会先登记你观察什么事件和执行什么方法,当这个事件发送到EventBus时,EventBus查找登记的方法反射调用执行即可;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass(); //获取观察者Class
//获取观察者类中所有的注解方法,保存在List中
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) { //循环执行subscribe()方法
subscribe(subscriber, subscriberMethod);
}
}
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //封装信息
// 根据监听的事件类型从缓存中获取保存的CopyOnWriteArrayList
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>(); //缓存中不存在则创建并保存subscriptions
subscriptionsByEventType.put(eventType, subscriptions);
}
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;
}
}
//从typesBySubscriber中获取缓存的贯彻的事件类型,如果不存在则创建并保存观察者和观察的事件类型
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
}
简单的总结注册流程:
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> 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--;
}
}
}
}
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); //从缓存中获取Subscrober所观察的事件类型集合
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) { //循环遍历集合执行unsubscribeByEventType()
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber); //移除观察者
}
}
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);
}
.......
METHOD_CACHE.put(subscriberClass, subscriberMethods); //缓存获取到的信息
return subscriberMethods;
}
}
这里的操作基本框架中都会见到,首先根据Class从METHOD_CACHE中获取,若不存在则反射遍历Class获取所有符合条件的方法,将结果缓存在METHOD_CACHE中,这里使用METHOD_CACHE缓存是防止每一次注册时都需要反射获取,然后根据ignoreGeneratedIndex的值分别去加载观察方法,这里主要是是否使用索引查找,关于索引会在下一篇单独介绍,这里知道调用反射获取就好了;
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
methods = findState.clazz.getDeclaredMethods(); //反射获取所有方法
}
for (Method method : methods) { //遍历方法
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //判断修饰符Public
Class<?>[] parameterTypes = method.getParameterTypes(); //获取参数集合
if (parameterTypes.length == 1) { 判断参数长度是否为1
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); //获取并判断Subscribe注解
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0]; //取出参数类型,即观察的事件类型
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode(); //获取直接的执行线程
//封装所有信息在SubscriberMethod中,并保存在List中
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
}
}
}
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>(); //当前线程的事件对列
boolean isPosting; //是否正在发送事件
boolean isMainThread; //是否为主线程
Subscription subscription; //观察者信息
Object event; //事件
boolean canceled;
}
public void post(Object event) {
//获取当前线程的事件对列,保存发送的事件
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) { //判断当前线程是否正在发送
postingState.isMainThread = isMainThread(); // 赋值是否为主线程
postingState.isPosting = true;
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState); //循环便利对列,取出头部事件执行
}
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass(); //获取发送事件的Class
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); //获取当前类和其父类的集合
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) { //遍历所哟Class集合执行postSingleEventForEventType()
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else { //对于单独的Event直接执行postSingleEventForEventType
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
}
上述代码根据发送的Event是否有父类进行分别处理,对于存在父类或接口的调用lookupAllEventTypes()获取所有Class的集合,然后遍历集合中的Class分别执行发送事件,也就是不仅发送自己类型的Event页发送所有父类的event;对于单一的Event直接发送事件;
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass); //从缓存中获取Subscription集合
}
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;
}
}
return true;
}
return false;
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event); //POSTING默认在同一线程执行,所以直接执行事件
break;
case MAIN: // 对于指定Main线程方法,如果发送事件在Main线程直接执行,否则调用mainThreadPoster加入主线程对列
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED: //在主线程中排队执行
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
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);
}
}
整个EventBus的执行流程最复杂的地方就是事件的发送,但总的来说还是很好理解的,在发送事件后从缓存的观察方法中找到事件的监听方法,然后根据二者的线程要求选择合适的执行线程,然后反射执行方法即可,对于整个过程可能会有几个疑问,比如:isMainThread在什么时候设定的?mainThreadPoster是什么?为何可以切换线程?. backgroundPoster、asyncPoster如何实现功能?针对这几个对象都是在EventBus初始化时设置好的,下面简单分析下几个疑问:
postingState.isMainThread = isMainThread(); //Post方法中调用isMainThread()
private boolean isMainThread() {
return mainThreadSupport != null ? mainThreadSupport.isMainThread() : true;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper(); // 此处的looper在创建传入的Looper.getMainLooper()
}
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper, 10);
}
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue(); //初始化主线程中的对列
}
queue.enqueue(pendingPost); //加入事件对列
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) { //发送事件
throw new EventBusException("Could not send handler message");
}
}
@Override
public void handleMessage(Message msg) {
eventBus.invokeSubscriber(pendingPost); //最终还是调用eventBus.invokeSubscriber()
}
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); //调用线程池执行Runnable
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
eventBus.invokeSubscriber(pendingPost); //取出事件调用 eventBus.invokeSubscriber()发送事件
}
}
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event); //保存发送的Event事件
}
post(event); //按正常流程发送事件
}
2.粘性事件的接收, 在注册观察粘性事件时,如果Subscribe注解中标名接受粘性事件,则直接获取缓存中匹配的粘性事件并直接发送,所以在观察粘性事件时只要注册成功立刻会执行方法
if (subscriberMethod.sticky) { 如果注册的方法为sticky
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) { //遍历stickyEvents集合,获取符合的Event发送事件
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent); //最终调用postToSubscription()处理事件
}
}
在上面的分析中我们知道,EventBus使用findUsingReflectionInSingleClass()通过反射获取每个观察者的注解方法,用官方的话说早期是因为效率所以选择反射放弃注解,EventBus 3.0后使用注解生成索引文件,在加载时不需要使用反射更好的提高了EventBus的效率;
implementation 'org.greenrobot:eventbus:3.1.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.1.1'
kapt {
arguments {
arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex')
}
}
EventBus.builder().addIndex(new MyEventBusIndex()).ignoreGeneratedIndex(false).installDefaultEventBus();
@Subscribe
public void action(TypeEvent event) {
}
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void actionTwo(TypeEvent event) {
}
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; // Key = Class Value= SubscriberInfo
static { . //初始化HashMap保存信息
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("action", TypeEvent.class), //封装信息到SimpleSubscriberInfo中并保存
new SubscriberMethodInfo("actionTwo", TypeEvent.class, ThreadMode.MAIN, 0, true)
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info); //保存到HashMap中
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
注解生成类中,首先创建Map集合用于保存所有的注解方法,在static代码块中将注解方法名、事件类型、线程信息、优先级、是否为粘性事件都封装在SubscriberMethodInfo中,然后将一个类中的创建的所有SubscriberMethodInfo保存在集合中,最后集合封装在SimpleSubscriberInfo中,并以class为Key保存在在Map中;
EventBus.builder().addIndex(new MyEventBusIndex())方法中,将MyEventBusIndex实例保存在subscriberInfoIndexes中,然后在创建EventBus时使用subscriberInfoIndexes索引创建SubscriberMethodFinder的实例,通过之前的学习知道SubscriberMethodFinder负责查找所有的注册方法,在SubscriberMethodFinder.findSubscriberMethods()中如果设置ignoreGeneratedIndex为false,则执行findUsingInfo(),下面看看findUsingInfo()是如何获取注解类中保存的方法的
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState); //获取SubscriberInfo信息
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); //获取Method集合
for (SubscriberMethod subscriberMethod : array) { //遍历Method集合
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod); //直接保存Method
}
}
} else {
findUsingReflectionInSingleClass(findState);//没找到的继续使用反射查找
}
}
从上面的代码中看到首先从getSubscriberInfo获取subscriberInfo,可以推测到这里获取的subscriberInfo就是前面注解类中创建并实例化的SimpleSubscriberInfo,然后获取其中保存的SubscriberMethod集合,遍历集合中的SubscriberMethod保存在findState.subscriberMethods并返回,这样就获取到所有的注解方法集合了,下面看一下getSubscriberInfo如何获取的。
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) { //遍历传入的subscroberInfoIndexes集合
SubscriberInfo info = index.getSubscriberInfo(findState.clazz); //从MyEventBudIndex中保存的集合中获取SubscriberInfo实例
if (info != null) {
return info;
}
}
}
代码逻辑很简单,上面是使用class为Key缓存的这里遍历所有的subscroberInfoIndexes然后以class为键取出SubscriberInfo实例,到此注解生成类和解析方法的过程就分析完毕了,在编译时就创建并保存好所有方法在执行时只需要直接获取即可。
@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe”) //声明处理的注解类型
@SupportedOptions(value = {"eventBusIndex", "verbose”}) //配置的索引文件的参数
public class EventBusAnnotationProcessor extends AbstractProcessor {
public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex";
public static final String OPTION_VERBOSE = "verbose";
private final ListMap<TypeElement, ExecutableElement> methodsByClass = new ListMap<>(); //保存类中的方法
private final Set<TypeElement> classesToSkip = new HashSet<>(); //保存跳过的不符合要求的方法
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX); // 获取build.gradle中配置的文件信息
collectSubscribers(annotations, env, messager); //获取类中的所有注解方法,保存在methodsByClass中
checkForSubscribersToSkip(messager, indexPackage); //跳过不符合条件的方法保存在classesToSkip中
createInfoIndexFile(index); //创建类文件
}
EventBusAnnotationProcessor中使用APT根据注解生成代码,整个类中的方法不多,出去判断和写入的逻辑后上面为主要的流程代码,真个过程分四步,其余细节可自行查看源码:
int period = index.lastIndexOf('.');
String myPackage = period > 0 ? index.substring(0, period) : null; //解析包名
String clazz = index.substring(period + 1); //解析类名
观察方法和观察类的缓存
在EventBus中使用subscriptionsByEventType以EventType为Key缓存每种事件类型对应的所有观察者和观察方法;再使用typesBySubscriber以Subscriber为Key保存每个观察者观察的事件类型,简单的说就是Subscriber --> EventType --> fun() 这样将所有的方法、观察者、事件类型联系起来,在发送事件时和注销时查找都很便捷
ThreadLocal区分每个线程
在处理事件的每个线程上都有各自的ThreadLocal 实例,在ThreadLocal中创建PostingState实例用于保存每个线程的信息,这样在事件发送时线程等信息就会伴随事件,并根据线程确定处理方式,线程彼此互布影响。
在EventBus的内部保存着主线程handler、backgroundPoster、asyncPoster实例,并且每个实例中保存着各自的对列,在事件发生时根据发送事件的线程和注解方法的信息,将事件加入到各自的对列中然后各自执行事件。
对EventBus的学习就到此结束了,本篇文章也是对之前笔记的整理,希望对学习EventBus和学习框架的同学有所帮助!