1、概述
EventBus是针对Android优化的发布-订阅事件总线,简化了Android组件间的通信。EventBus以其简单易懂、优雅、开销小等优点而备受欢迎。关于其如何使用网上有很多教程,也可以从其官网中了解到基本用法。其中整体事件流程的走向基本上可以用官网的一张图来说明,如下:
publisher通过post来传递事件到事件中心Bus,Bus再将事件分发到Subscriber。其本质是一个观察者模型,最核心部分也就是Bus如何接收消息和分发消息。
2、基本概念:
在讲解源码之前,先说一下EventBus需要关注的点 - EventBus支持的四种线程模式(ThreadMode)。如我们常常采用以下方式使用
@Subscribe(threadMode = ThreadMode.POSTING)
public void getEventBus(Integer num) {
if (num != null) {
Toast.makeText(this, "num" + num, Toast.LENGTH_SHORT).show();
}
}
其中@Subscribe(threadMode = ThreadMode.POSTING)
也可以写成@Subscribe
。
1. POSTING(默认):事件在哪个线程发布,就在哪个线程消费,因此要特别注意不要在UI线程进行耗时的操作,否则会ANR。
2. MAIN:事件的消费会在UI线程。因此,不宜进行耗时操作,以免引起ANR。
3. BACKGROUND:如果事件在UI线程产生,那么事件的消费会在单独的子线程中进行。否则,在同一个线程中消费。
4. ASYNC:不管是否在UI线程产生事件,都会在单独的子线程中消费事件。
除此之外EventBus还支持粘性事件,即发送一个未注册的粘性事件,注册者会在完成注册之后收到这个粘性事件。
3、源码解析
正如官网图和上文所说,EventBus最核心的部分就是其消息注册和分发中心,如何将消息注册者和消息接收这绑定起来达到准确的分发,这个当是难点。接下来我们通过起源码来逐一分析和解读。从官网中下载EventBus的源码,可以知道其源码并不是很多,整体结构基本如下:
源码结构相对来说是非常清晰了,大佬就是大佬。
我们开始使用EventBus的时候都会采用如下方式注册使用
EventBus.getDefault().register(this);
通过查看getDefault方法
/** Convenience singleton for apps using a process-wide EventBus instance. */
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;
}
发现是一个“双重校验锁”的单例模式。
查看EventBus
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
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;
}
构造者,里面会初始化一些基础变量。
3.1注册
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();//1
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//2
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);//3
}
}
}
- 获取注册的者上下文,比如说Activity
- 通过注册者的上下文查找注册的事件方法
- 将注册者和注册的事件绑定起来,即完成了注册,可以查看
subscribe
方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//1
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//2
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);//3
} 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;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {//4
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
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();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
- 将注册者和事件消费方法封装起来,这里才是真正的绑定。
- 就像上述注册者和事件消费方法是1:N的关系。一个Event与注册者之间也是1:N的关系。因为一个Event可能会被不同的Activity注册。也就是说Event、注册者、事件消费方法的关系是:1:N:M(其中M、N均大于等于1)。
- 注册者(比如MainActivity.this)与事件消费方法(SubscriberMethod)的关系,我们封装到了Subscription(s)中了。而Event和Subscription(s)的关系,我们通过HashMap保存,key为event.class,value即Subscription(s)。
- 黏性事件的处理。
3.2发布与消费
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;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);//1
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
- 通过前面的状态判断,走到这一步真正消费事件。继续往下走该方法,我们可以发现其最终是调用方法
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);
}
}
从代码可以知道,最终会通过判断线程,来依次消费事件。
查看方法invokeSubscriber(subscription, event);
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);
}
}
可以看出最终是以反射的方式。
3.3 反注册
注册消费完事件后,我们需要反向注册,类似如我们广播的使用。
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--;
}
}
}
}
可以看到是依次移除掉subscriptions
列表。
4、总结
从整个代码流程来看,基本上没什么难点,用起来也非常方便。
但在使用过程中,像我这种菜鸟发现了两个问题
-
其消息给人感觉是一种乱跳的感觉,因为其采用注解的方式,这点感觉对业务逻辑梳理并不一定占有优势,就拿Android Studio来说,居然会提示该方法无处使用,如下图:
no_use.png - 采用反射方法
invokeSubscriber
来消费事件,效率如何。
网友评论