概况
EventBus一直都知道这个库,但是没有接触,看到国内 Top500 Android 应用分析报告使用情况,觉得有必要掌握此库。EventBus是android的一个用于消息传递的库,属于订阅者模式,让发布者和订阅者解耦。本文通过对EventBus的使用,分析其内部实现。
简单使用
关于EventBus的使用不是本文的重点,EventBus的使用请查看EventBus使用详解。
做个比喻
为了更好的理解EventBus这个库的流程,以下将根据下图做一个比喻。
如上图,EventBus充当苹果公司销售部的角色。当苹果公司(Publisher发布者)生产(调用post方法)出某一款手机(EventType)时,消息会传递到销售部,由销售部告诉用户(Subscriber订阅者)。例如用户想购买iphone8,该用户必须要预约(方法被@Subscriber注解),当销售部接到公司说iphone8开始售卖了,那么只有预约手机的用户可以购买(订阅方法被调用)。
阅读源码
EventBus.getDefault().register(this);
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus(); //调用默认构造函数构建单例
}
}
}
return defaultInstance;
}
看new EventBus做了什么初始化工作:
public EventBus() {
this(DEFAULT_BUILDER);
}
空构造函数引用带参数的构造函数,参数类型是EventBusBuilder:
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;
}
通过使用默认构造器EventBusBuilder,默认配置了一些参数,部分重点参数会接下来在阅读源码中提到。
register(Object subscriber)
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
调用register方法,可以看到参数是一个Object,EventBus支持Activity、Fragment、Service、BroadcastReceiver等之间传递消息。
调用subscriber.getClass()返回subscriber对象的运行时类的Java.lang.Class。
根据subscriber运行时的类获取该类所以的SubscriberMethod,就是该类被@Subscriber注解的所有方法。
通过循环该类所有的的被注解的方法,通过调用subscriber(subscriber, subscriberMethod)方法绑定subscriber和subscriberMethod的关系。
subscribe(subscriber, subscriberMethod);
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取subsrciberMethod传递的自定义EventType参数的运行时的类
Class<?> eventType = subscriberMethod.eventType;
//Subscription用于绑定subscriber和sucriberMethod,一个订阅者可以有多个subscriberMethod
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根据EventType的运行时类取到该类所有的subscriptioins,subscriptionsByEventType是HashMap中的key
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
//若根据EventType找不到subscriptions,则eventType作key,subscriptions作value添加到subscriptionByEventType中。
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
//已经存在newSubscription,抛出异常该订阅者已经注册,不可重复注册同一个subscriber
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
//循环subscriptions,根据标记优先级的priority从高到低,将新的subscription插入到subscriptions中
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//typesBySubscriber是一个HashMap,根据subscriber做key,获取该subscriber对应的所有的订阅事件的类型
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
//该订阅者之前的订阅事件类型列表为空,则将当前订阅类型添加到typesBySubscriber中
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//如果该方法被标识为sticky事件
if (subscriberMethod.sticky) {
if (eventInheritance) { eventInheritance标识是否考虑EventType的类层次结构
//循环所有的sticky黏性事件
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();
heckPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
关键代码以上都有注释。
黏性事件,主要使用场景是:当订阅者尚未创建,先调用EventBus.getDefault().postSticky()方法发送一个sticky事件,该事件会被stickyEvents缓存起来,当订阅该事件的类调用register()方法时,同样可以收到该事件。而调用EventBus.getDefault().post()则必须先调用register(),才能收到事件。
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方法;
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//根据@subscriber中threadMode进行区分,POSTING为当前线程执行,
//MAIN为主线程,BACKGROUND为子进程,ASYNC为异步执行。
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);
}
}
invokeSubscriber(subscription, event)
下面我们来分析threadMode = ThreadMode.POSTING的情况,调用invokeSubscriber()方法,其他情况类似。
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.getDefault().register(this)小结
回到苹果公司卖手机的例子,EventBus.getDefault().register(this)中,this指的就是买手机的顾客,顾客通过调用此方法告诉销售部说我想登记买手机,登记的流程包括:
A.找到该用户所有的购买手机型号的预约信息(根据subscriber找到所有subscriberMethorssubscriberMethodFinder.findSubscriberMethods(subscriberClass)
);
B.将新登记的预约信息和之前的预约信息做比对,如果之前没有预约信息subscriptions == null
,则添加到预约信息中;若之前有预约过的信息,检查是否有相同的预约信息(subscriber和subscriberMethod都一样)subscriptions.contains(newSubscription)
,若有,则预约失败,并且丢给用户一个臭脸throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);
。
C.添加到预约信息列表的时候,根据优先级由高到低,优先级相同插入到最后边的原则放入列表。
D.若用户想预约的手机不是新款手机,而是一款老手机subscriberMethod.sticky=ture
,则无需预约,可直接购买。
EventBus.getDefault().post(EventType);
post()和postSticky()的区别
发布者调用post(EventType)方法,若订阅者需要接收到此EventType,则订阅者必须要先注册并且注解接收此方法,否则收不到该方法;而调用postSticky(EventType),及时先发布者先发送消息,订阅者在注册事件,也能收到消息。
例如苹果公司出iphone8新款手机了,那么用户需要先预约手机,不预约的不能购买,此时苹果公司调用post(iphone8)方法;如果苹果公司推出的是一款旧手机iphone5,iphone5无须预约,顾客到店就可以直接购买,这时候调用postSticky(iphone5)方法。
post(EventType);
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;
}
}
}
将当前发送事件添加到待处理的事件列表,如果当前不是在发送事件状态,则循环事件列表,调用postSingleEvent(eventQueue.remove(0), postingState)
发送每一个事件。
postSingleEvent(eventQueue.remove(0), postingState);
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//当需要处理事件的继承关系时
if (eventInheritance) {
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);
}
//如果该事件没有subscriber
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
主要处理调用postSingleEventForEventType()遇到的错误。
postSingleEventForEventType()
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根据eventType运行时的类找到所有的subscripitions
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;
}
以上代码主要是将该事件传递给订阅了该事件的订阅者,每一个订阅者都尝试将该事件作为参数,调用所有的订阅的方法。
post()方法小结
以上逻辑并不复杂,就直接做比喻好了:
苹果公司发布iphone8的流程:
A.在发布iphone8的时候,先检查一下iphone8之前的手机是否都已经发布了,将iphone8添加到发布列表中;
B.若当前没有手机在发布流程中,则从发布列表中取出接下来待发布的手机,假设iphone8之前的都已经发布,那iphone8就进入发布流程;
C.根据iphone8找出所有iphone8的预约信息,预约了iphone8的用户可以购买手机;
D.处理特殊的情况:如果没有人预约,或者之前预约的人不购买。
总结
以上分析了EventBus的两个主要入口代码,做的比喻可能并不恰当,但应该能对理解源码的逻辑有很大的帮助。