Eventbus源码之学习总结
转载请注明出处
https://blog.csdn.net/dreamsever/article/details/80031988
网上关于Eventbus的源码解析有很多,但是我还是要自己写一下,看别人的源码解析,也许可以让我看懂大致,但是自己使用自己的语言总结分析,会让自己记录的更深,体会的更深。
也许看EventBus官方给我们的图,让我们更好理解一些,可以把它翻译为事件总线或者事件巴士。首先发布者,就是我们执行EventBus.getDefault().post(new MyEvent());的地方。new MyEvent()就是一个事件,然后通过EventBus这个总线或者巴士,我们可以把它当作公交车或者流水线把事件传递到订阅这个事件的地方。EventBus既是发布人,但同时EventBus还有点类似与送报小哥角色,谁订阅报纸了,把今天新发布的报纸给他们送过去。
然后来看看EventBus这个多角色人物的构建过程
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
多线程安全的单例模式创建出EventBus 对象,继续去看构造方法
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
使用Builder方式新建一个EventBus,Builder模式大家都不陌生,通知,对话框,等等很多地方都用的这种形式。不过这里却有点不一样,这个默认的Builder就是把一些属性给我们默认好了,直接去拿。看下面的构造方法,我相信你看到后第一感觉就是new了好多集合,泛型,未知集合。。感觉自己都没有信心往下看了。我想说先不要看这些集合,先跳过这些集合往下看
//
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
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;
}
先看这里,先看什么呢?先看注册方法。我们都知道Eventbus的使用,就是在Activity中的onCreate()/onStart()方法注册,然后在onDestroy()/onStop()方法中取消注册。然后在写一个订阅方法,类似下面:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(GlobalEvent event) {
//do some thing
}
就ok了,那我们先去看看注册方法参数,
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历所有的订阅方法,执行订阅
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
很明显就是我们的订阅对象,比如是Activity或者Fragment,这里是用Object对象接受的也就意味着是所有的对象都可以。这里的findSubscriberMethods就是获取这个订阅对象的订阅方法,至于具体怎么执行的先不去看,先继续往下看subscribe(subscriber, subscriberMethod);
//传入参数为订阅对象和订阅方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//订阅方法的事件类型,具体指的是什么呢?其实我们上面的GlobalEvent 就是一个事件类型
Class<?> eventType = subscriberMethod.eventType;
//一个订阅者和一个订阅方法构成一个订阅事务,
//这个订阅事务就是一个订阅者,订阅方法,被订阅事件的组合。
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//下面你看到了什么?subscriptionsByEventType,仿佛上面有,
//这里是根据事件类型获取一个订阅事务的集合,那么我们仿佛明白了,
//subscriptionsByEventType是什么鬼,就是key为订阅方法类型,
//value为所有该订阅方法类型的订阅事务集合
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//订阅事务集合为空新建
subscriptions = new CopyOnWriteArrayList<>();
//给subscriptionsByEventType添加进去
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) {
//根据事件优先级将订阅事务添加进入订阅事务集合
subscriptions.add(i, newSubscription);
break;
}
}
//这里你看到了什么?typesBySubscriber好像上面也有,key为订阅对象,
//value为事件类型,也就是存放的每一个订阅者里面绑定的订阅时间类型的集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//避免孤军深入,这里sticky事件先不考虑
if (subscriberMethod.sticky) {
。。。
}
}
现在我们应该都知道上面的几个集合都是什么了。来我们在回顾下:
//key为事件类型,value为所有该订阅事件类型的订阅事务的HashMap
subscriptionsByEventType = new HashMap<>();
//key为订阅对象,value为事件类型的HashMap
typesBySubscriber = new HashMap<>();
然后看一下unregister方法
public synchronized void unregister(Object subscriber) {
//拿到所有的事件类型
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
//取消订阅事件
unsubscribeByEventType(subscriber, eventType);
}
//最后把typesBySubscriber里面subscriber为键的这个键值对移除
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
//
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//先拿到所有订阅这个事件类型的订阅者,这里面一定包含subscriber
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);
//如果在这里找到subscriber就去把他移除
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
至此,EventBus的注册和取消注册就说完了,下面该发布一个事件,看看它到底是如何发布和如何传递这个事件的。
EventBus.getDefault().post(new MyEvent());
//这里先用ThreadLoacal方式定义了一个PostingThreadState,关于ThreadLoacal不了解的可以先去学习下,它是用来保证变量在每个线程中的唯一性的,也是线程安全的一种形式。这里就是保证当前线程中PostingThreadState这个变量是本线程中的跟其他线程无关,其他线程也不会修改到它,类似Handler机制,保证PostingThreadState里面的eventQueue不会被其他线程操作。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
发布事件
public void post(Object event) {
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;
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//拿到Event的class
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//eventInheritance默认为true,默认支持event继承
if (eventInheritance) {
//去寻找所有的eventClass,包括父类和子类
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//遍历执行postSingleEventForEventType方法,带有返回值
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) {
post(new NoSubscriberEvent(this, event));
}
}
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//拿到所有该事件的订阅事务
subscriptions = subscriptionsByEventType.get(eventClass);
}
//不为空表示找到相应的事务,返回true,否则返回false赋值给subscriptionFound
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;
}
来到这我们貌似已经看到结果了,就是根据threadMode分发执行不同的方法去处理,以我们常用的MAIN为例执行了invokeSubscriber
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);
}
}
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的基本流程就执行完了注册反注册,发布发布到执行事件,但是还没有结束,有一次面试被问到,EventBus中使用到了编译时注解来提高效率,你知道吗?我很懵逼的说不知道,看到现在,问你同样的问题,你应该也不知道在哪吧,那我们去找找编译时注解。
请看这里http://greenrobot.org/eventbus/documentation/subscriber-index/
原来我们自始自终就没有用到这个新功能,看看这个新功能是如何使用的,上面官网说的很详细,感兴趣可以去看下
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
}
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}
看到这里EventBus自动为我们生成了MyEventBusIndex文件
初始化EventBus
EventBus mEventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
addIndex方法
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
//就是给subscriberInfoIndexes添加进去一个index
subscriberInfoIndexes.add(index);
return this;
}
然后构建的时候subscriberMethodFinder 的构造方法使用到了,为什么是subscriberMethodFinder 用到了了呢?这个我们继续往下看就知道了
EventBus(EventBusBuilder builder) {
...
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
...
}
MyEventBusIndex()就是我们编译生成的SubscriberInfoIndex ,这里称为索引。可以大致看一下里面都做了什么,等会我们要用到,SUBSCRIBER_INDEX是一个HashMap,key为订阅者,value为SubscriberInfo,这里使用的是SubscriberInfo的子类SimpleSubscriberInfo,包含
private final Class subscriberClass;
private final SubscriberMethodInfo[] methodInfos;
订阅者,订阅方法数组等等这些关于订阅者的信息
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(com.example.test.first.fragment.SticyNavFragment.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventMainThread",
com.example.test.first.eventbus.StickyNavRefreshEvent.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onMessageEvent",
com.example.test.first.eventbus.StickyNavRefreshEvent.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(com.example.test.first.fragment.TestEventBusStubFragment.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onMessageEvent",
com.example.test.first.eventbus.StickyNavRefreshEvent.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onMyOwnEvent", com.example.test.first.eventbus.StubEvent.class,
ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(com.example.test.first.fragment.PartThreeFragment.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventMainThread",
com.example.test.first.eventbus.AppBarOffsetEvent.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
好,到这里我们索引也生成好了,那么在哪个地方使用的这些索引呢?答案是在注册的时候
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);这个findSubscriberMethods方法我们并没去看,现在正是看它的时候。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//先去缓存中寻找
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否不顾索引是否生成
if (ignoreGeneratedIndex) {
//不管有没有索引,都去使用反射,而不去使用索引,
//用户特殊需求设置的,这里默认为false
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//ignoreGeneratedIndex默认为false,所以会来到这里,重点findUsingInfo方法
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 ,这里还使用到了变量池FIND_STATE_POOL来提升性能
FindState findState = prepareFindState();
//初始化clazz等
findState.initForSubscriber(subscriberClass);
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);
}
//双重检查机制,检查是否要把这个方法添加进去
这里要考虑三种情况:
第一种:子类没有复写父类中的订阅方法,并且在子类中没有相同的Event事件
第二种:子类没有复写父类中的订阅方法,但是,类中含有订阅同一个Event的订阅方法,也就是对相同的事件有两个方法去接受它,这种情况虽然不建议,但是你非要用也不阻止你,也是可以用的
第三种,子类复写了父类的方法
第一种情况,先添加子类的订阅方法,每次的 (existing == null) 直接返回true。
第二种情况:出现了一个existing 不为null,因为同一个事件出现了两次,肯定是属于Method的,先进行了一次checkAddWithMethodSignature方法判断,往下看
boolean checkAdd(Method method, Class
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
现在已经把所有的方法都添加进去了,来到了getMethodsAndRelease(findState);这里其实就是返回List subscriberMethods ,还做了其他操作,给FIND_STATE_POOL这个变量池赋值,变量池可以防止大量的变量被新建而占用内存。
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;
}
到此我们就把
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
这句代码分析完了。分析起来还是有点困难的,假如你看的不是很理解,建议可以写一下代码,具体断点看看是不是像分析的那样运行的,这样也许会体会的更深,理解起来更容易。
总结:
EventBus源码还是非常值得研究学习的,能做到装机量1亿+的库肯定有它的厉害之处。使用者使用起来很便捷,代码量不多,性能又不错。里面有很多让我们学习的地方,比如反射,编译时注解,变量池,线程安全的处理。还有一些很多小的细节拿上一个Object existing = anyMethodByEventType.put(eventType, method);,在put的同时拿到上一个被替换的对象,使用起来很巧妙,methodKeyBuilder.setLength(0);来做到StringBuilder的复用等等。
参考链接
https://www.cnblogs.com/bugly/p/5475034.html
https://juejin.im/entry/58710e9eb123db4a2eb866c4