EventBus 是一个在 Android 开发中使用的发布/订阅事件总线框架
EventBus...
-
简化组件之间的通信
- 解耦事件发送方和接收方
- 能够很好地处理Activities、Fragment和后台线程
- 避免复杂和容易出错的依赖关系和生命周期问题
使您的代码更简单
快
体积小(~60k jar)
在实践中已经被超过10亿次安装的应用程序证明
......(官方的王婆卖瓜自卖自夸)
注册:
EventBus.getDefault().register(this)
public void register(Object subscriber) {
//1.获取订阅者的Class对象
Class> subscriberClass = subscriber.getClass();
// 判断当前订阅者是否为匿名类(因为在匿名类中,订阅注解是不可见的,查找方式必须是反射方式,这个后面会详细说明)
boolean forceReflection = subscriberClass.isAnonymousClass();
//2.通过反射获取订阅者中开发人员定义的处理事件的方法集合
List subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass, forceReflection);
//3.将获取的订阅者和订阅者中处理事件的方法按照一定的规则相结合并存储到EventBus内部
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
register这个方法主要可以分为三部分。
第一步,获取订阅者的Class对象,这个订阅者通常就是我们写的Activity或者Fragment等。
第二步,findSubscriberMethods
方法是关键,通过我们第一步拿到的订阅者的Class对象,反射获取所有符合规则的处理事件的方法。后面单独看源码
第三步,将获取的订阅者和订阅者中处理事件的方法按照一定的规则相结合并存储到EventBus内部
第一步很简单,分析第二步的findSubscriberMethods
方法:
List findSubscriberMethods(Class> subscriberClass, boolean forceReflection) {
String key = subscriberClass.getName();
List subscriberMethods;
//首先看缓存中是否已经存在该订阅者的处理方法的集合
synchronized (METHOD_CACHE) {
subscriberMethods = METHOD_CACHE.get(key);
}
if (subscriberMethods != null) {
return subscriberMethods;
}
//查找事件处理方法有索引方式和反射方式两种
if (INDEX != null && !forceReflection) {
//索引方式查找,其实一般默认进入当前 if 逻辑
subscriberMethods = findSubscriberMethodsWithIndex(subscriberClass);
if (subscriberMethods.isEmpty()) {
//索引方式查找不到,依旧通过反射查找
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
}
} else {
//反射方式查找
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
}
//如果当前订阅者中的处理事件方法集合为空,则抛出异常;反之则加入缓存中并返回
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
synchronized (METHOD_CACHE) {
//加入缓存
METHOD_CACHE.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
索引是什么,我们平常使用EventBus的时候从来都没用过这个东西,其实在EventBus3.0之前是没有索引这个东西的,第二步获取方法集合走的是 findSubscriberMethodsWithReflection(subscriberClass); 这个方法。那为什么在3.0之后加上了索引呢?其实答案很简单,就是因为反射获取方法集合是比较低效的,使用反射所耗费的时间开销比较大。如果使用索引,EventBus的性能会提升一倍。具体如何在你的代码中使用EventBus的索引,可以参考 [greenrobot官网中index的介绍] ,实际应用中,我们使用EventBus根本没添加过索引相关的配置,所以最终我们还是走反射逻辑(http://greenrobot.org/eventbus/documentation/subscriber-index/) 。
既然通过反射查找,接着看findSubscriberMethodsWithReflection
方法
private List findSubscriberMethodsWithReflection(Class> subscriberClass) {
List subscriberMethods = new ArrayList();
Class> clazz = subscriberClass;
HashSet eventTypesFound = new HashSet();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
int modifiers = method.getModifiers();
//方法为public,且没有final,static等修饰符
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) {
String methodName = method.getName();
Class> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
ThreadMode threadMode = subscribeAnnotation.threadMode();
subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
}
}
}
clazz = clazz.getSuperclass();
}
return subscriberMethods;
}
该方法很明确,就是通过反射获取订阅者所有方法集合后,遍历所有方法,查找符合规则的方法 ,而规则无非就是这些方法必须加 @subscribe 注解,参数必须有且只有一个等等
进入第三步,遍历第二步获取的方法集合,调用 subscribe(subscriber, subscriberMethod)
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
subscribe(subscriber, subscriberMethod)是最后一步的核心代码
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取方法中的事件类型
Class> eventType = subscriberMethod.eventType;
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
//将订阅者和方法绑在一起形成一个Subscription对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
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优先级,将上面由订阅者和方法绑在一起形成的Subscription对象加入subscriptions集合中
synchronized (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;
}
}
}
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//如果方法可以接收sticky事件
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
}
}
这段代码虽然比较多,但看下来,功能就是初始化EventBus这个单例的的全局变量,主要是subscriptionsByEventType
,typesBySubscriber
, 通过名字我们也可以知道,都是map。
其中,subscriptionsByEventType
的key存的是eventType,value存的是Subscription的集合,记录了每一个相同的事件类型所关联的一切注册者和他们的处理事件的方法。
typesBySubscriber
的key是subscriber订阅者,value是eventType的集合,它记录了每一个订阅者所注册(care)的一切事件类型。
subscribe
方法最后对sticky粘性事件做了处理,再次贴下后面的代码
if (subscriberMethod.sticky) {
if (eventInheritance) {
//遍历stickEvents这个map
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class> candidateEventType = entry.getKey();
//如果找到了跟这个方法的eventType一致或是其子类的stickEvent
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
//发送事件,本质就是通过反射直接调用这个方法
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
什么是粘性事件?
简单讲,就是在发送事件之后再订阅该事件也能收到该事件。Android中就有这样的实例,也就是Sticky Broadcast,即粘性广播。正常情况下如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的Receiver,这时接收者便无法接收到 刚才的广播,为此Android引入了StickyBroadcast,在广播发送结束后会保存刚刚发送的广播(Intent),这样当接收者注册完 Receiver后就可以接收到刚才已经发布的广播。这就使得我们可以预先处理一些事件,让有消费者时再把这些事件投递给消费者.
EventBus也提供了这样的功能,有所不同是EventBus会存储所有的Sticky事件,如果某个事件在不需要再存储则需要手动进行移除。用户通过Sticky的形式发布事件,而消费者也需要通过Sticky的形式进行注册,当然这种注册除了可以接收 Sticky事件之外和常规的注册功能是一样的,其他类型的事件也会被正常处理。
继续回到这段代码,首先判断了处理事件的方法是否有stick这个注解,如果存在则进入下一步。eventInheritance这个变量默认为true,代表事件是否具有继承性。可以这么理解,发送一个事件或者发送这个事件类型的子类,EventBus的处理事件方法中的参数类型是父类型,那么这个方法既可以收到父类型的事件,也可以收到子类型的事件。那么如果处理事件方法中的参数类型是子类型呢?那这个方法就只能接收子类型的事件,而无法接收父类型的事件,这点是在if (eventType.isAssignableFrom(candidateEventType))
这一行代码做的判断。isAssignableFrom这个方法是JDK中Class类中的一个方法,作用就是判断前者是否是与后者类型一致或前者是后者的父类。下面这个代码就很简单了checkPostStickyEventToSubscription(newSubscription, stickyEvent);发送事件,我们跟进去看一下:
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, Looper.getMainLooper() == Looper.myLooper());
}
}
这个方法的功能很简单,对事件做了非空校验,然后调用了postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper())
方法,这个方法内部就是通过反射调用method.invoke(subscription.subscriber, event)
来执行我们定义的对事件的处理方法。这个方法很重要,后续在post方法的时候着重分析,现在需要知道它的功能即可。看到这里,也就能明白,为什么在粘性事件发出后再注册的事件处理方法也可以接收到它了,因为之前发送的stickyEvent都会存入stickyEvents这个map类型的EventBus中的成员变量中,当我们注册调用register方法时,在register方法内部会直接通过checkPostStickyEventToSubscription(newSubscription, stickyEvent);来执行我们定义的粘性事件处理方法。
至此,终于说完了register这个方法,这个方法的内容看似只有短短几行,但它所做的事情还是很繁琐复杂的。