作者:萌果爱吃柠檬
转载地址:https://juejin.cn/post/7096415729934827551
随着 LiveData
和 Kotlin Flow
的出现,EventBus
已经慢慢过时了。不过 EventBus
源码的设计思想以及实现原理还是值得我们去学习的。
EventBus().getDefault().register(this)
首先 EventBus
的创建用到了 DCL 单例模式,源码如下:
public class EventBus {
static volatile EventBus defaultInstance;
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;
}
}
示例:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
}
/**
* threadMode 执行的线程方式
* priority 执行的优先级
* sticky 粘性事件
*/
@Subscribe(threadMode = ThreadMode.MAIN,priority = 50,sticky = true)
public void test1(String msg){
// 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
Log.e("TAG","msg1 = "+msg);
}
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100,sticky = true)
public void test2(String msg){
// 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
Log.e("TAG","msg2 = "+msg);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
EventBus().getDefault().post("text")
register()
方法源码:
public void register(Object subscriber) {
// 获取 class 对象
Class<?> subscriberClass = subscriber.getClass();
// 遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
这里的 subscriber
就是我们的 MainActivity
,第一行代码很简单,就是获取 MainActivity
的 class
对象。
第二行代码的意思:遍历这个 class 的所有方法,将含有 @Subscribe
注解的方法找出来,并将它们封装成 List
。SubscriberMethod
的成员变量如下所示:
我们来看下 findSubscriberMethods
源码:
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;
}
}
这里的 subscriberClass
就是我们的 MainActivity
的 class
对象。
ignoreGeneratedIndex
这个属性表示是否忽略注解成器生成的 EventBusIndex.class
文件, 我们只要知道 ignoreGeneratedIndex
默认是 false
就行了。
所以会走 findUsingInfo(subscriberClass)
方法,我们来看下源码:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
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);
}
这里我直接说结论,因为我们没有使用编译时注解生成 EventBusIndex.class
文件,所以 findState.subscriberInfo
为空,所以这里会走 findUsingReflectionInSingleClass(findState)
方法,最终就是通过反射去找。我们来看下 findUsingReflectionInSingleClass
源码:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
......
}
}
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 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()));
}
}
}
}
}
}
第一行代码 findState.clazz.getDeclaredMethods();
就是通过反射获取订阅类的所有方法。
然后遍历 methods
,判断方法的修饰符是不是 public
,然后获取方法参数的 class
,并判断是不是只有一个参数,并且还判断是否被 @Subscribe
修饰。method.getParameterTypes()
就是获取方法的参数类型。String msg
获取到的就是 String.class
,如下:
遍历完 methods
之后,就会将所获得的属性添加到 subscriberMethods
中,如下:
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
最后通过 findState
将 subscriberMethods
返回回去。也就是 register()
方法中的List
。
public void register(Object subscriber) {
// 获取 class 对象
Class> subscriberClass = subscriber.getClass();
// 遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
subscriberMethodFinder.findSubscriberMethods()
方法就是通过反射去解析注册者(MainActivity
)的所有方法。并且找出被 @Subscribe
注解修饰的方法,然后通过 Annotation
解析所有需要的参数(包括 threadMode
、priority
、sticky
、参数的类型 eventType
、method
)。最后把这些参数封装成 SubscriberMethod
添加到集合返回。
接下来我们来分析 subscribe
方法:
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
我们先看一下 subscribe()
方法的参数,subscriber
参数就是我们的 MainActivity
,subscriberMethod
参数是我们上面对方法的封装。
我们来看一下 subscribe()
方法的源码:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// eventType 是方法参数的 class,也就是 String.class
Class<?> eventType = subscriberMethod.eventType;
// 又封装了一个 Subscription 对象 subscriber : MainActivity, subscriberMethod : 解析好的方法
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 第一次拿为空,put
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
......
}
// 优先级排序
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;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 粘性事件的处理
```
}
这里又封装了一个 Subscription
对象,成员变量就是:
subscriber
参数就是我们的 MainActivity
subscriberMethod
参数是我们上面对方法的封装接下来用 subscriptionsByEventType
将 Subscription
对象存起来,subscriptionsByEventType
是个 Map
,如下:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
key
: Class>
(参数类型,也就是 Strin.class)value
: CopyOnWriteArrayList
(线程安全的 ArrayList
,里面是 Subscription
)subscriptionsByEventType.get(eventType)
第一次拿肯定为空,所以会调用 subscriptionsByEventType.put(eventType, subscriptions)
。到这里,subscriber
方法就分析好了。
解析所有的 subscriberMethod
的 eventType
,然后将它们封装成 Map
类型的 subscriptionsByEventType
,key
是 eventType
,value
是 Subscription
的列表,Subscription
包含两个属性 subscriber
和 subscriberMethod
。
其实根据上面那个图,我们很容易猜出 post()
方法肯定会遍历 subscriptionsByEventType
,找到一样的 eventType
,然后去执行 CopyOnWriteArrayList
列表里面的 Subscription
对象里面的 subscriberMethod
对象里面的 method
。
我们来看下 post()
方法源码:(经过一系列调用)
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
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;
}
这里的 event
就是 post("text")
里面的 text
,所以 eventClass
就是 String.class
,然后执行 subscriptions = subscriptionsByEventType.get(eventClass)
,就会将我们上面图片中的 value
取出来。然后遍历 subscriptions
,执行 postToSubscription(subscription, event, postingState.isMainThread)
方法,我们看下这个方法的源码:
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);
}
}
我们的 threadMode
有四种形式:
POSTING
:同一个线程,在哪个线程发送,就在哪个线程执行。MAIN
:在主线中执行。BACKGROUND
:子线程,如果发送事件是主线程,那么久调用线程池切换线程来执行方法。ASYNC
:异步线程,无论发布时间是主线程还是子线程,都有通过线程池切换线程执行。所以我们看到源码中,如果是 POSTING
方式就直接调用 invokeSubscriber(subscription, event)
执行方法。其他方式就可能需要调用线程池切换线程来执行订阅方法。
invokeSubscriber
源码如下:
void invokeSubscriber(Subscription subscription, Object event) {
try {
// subscriber 是 MainActivity,event 是 post 发送的 "text"
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
post()
方法会将 post(Object event)
里的 event
的 class
对象作为 key
,去 subscriptionsByEventType
这个 map
里面取出 key
跟 event
的 class
相同的 subscriptions
,然后遍历 subscriptions
去执行订阅方法。
unRegister()
方法就是将上面所产生的那些 map
清空
EventBus
执行订阅方法的原理主要是通过注解和反射。细节上用的的知识还是非常多的,比如 DCL 单例模式
、Builder 设计模式
、享元设计模式
(FindState
的对象池设计)、线程池
、Handler
、ThreadLocal
等。
并且还引入了注解处理器,使用这种方式可以不需要用到反射来查找订阅方法,优化了运行时性能。不过本文的源码分析分析的还是利用反射来查找订阅方法。
通过阅读 EventBus
源码,可以看出作者很多地方的精彩设计,很值得我们学习。本文只是分析了核心的 EventBus
源码,很多细节上的源码就直接跳过去了。