EventBus框架库代码走读,阿里Android开发面试解答

本文详细介绍了EventBus框架的注册(register)、取消注册(unregister)及发布(post)事件的过程。从源码层面解析了如何查找订阅者类中的订阅方法、创建并添加订阅到事件列表、以及根据线程模式分发事件。通过对EventBus的内部机制分析,加深了对Android事件总线理解。
摘要由CSDN通过智能技术生成
  • methodName);

}

}

}

clazz = clazz.getSuperclass();

}

if (subscriberMethods.isEmpty()) {

throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "

  • ON_EVENT_METHOD_NAME);

} else {

synchronized (methodCache) {

methodCache.put(key, subscriberMethods);

}

return subscriberMethods;

}

}

上面就是获取传入对象class的方法的方法。其中前半部分是先去缓存查找是否有这个类的记录,如果有直接返回,没有继续执行。当没有时继续走到Method[] methods = clazz.getDeclaredMethods();语句得到该类的所有方法;接着那个大for循环就是遍历这个类匹配符合封装要求的method;其中,if (methodName.startsWith(ON_EVENT_METHOD_NAME))用来判断方法名是不是以“onEvent”开头;接着if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0)用于继续判断是否是public且非static和abstract方法;if (parameterTypes.length == 1)用于继续判断是否是一个参数。如果都复合,才进入封装的部分;接着也比较简单,根据方法的后缀,来确定threadMode,threadMode是个四种情况的枚举类型(前面基础使用一篇解释过四种类型);接着通过subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));把method添加到subscriberMethods列表;接着通过clazz = clazz.getSuperclass();扫描父类的方法;接着while结束,扫描完后通过methodCache.put(key, subscriberMethods);将方法放入缓存,然后返回List<SubscriberMethod>的方法列表(订阅者方法至此查找完成)。

接着继续回到上一级方法:

private synchronized void register(Object subscriber, boolean sticky, int priority) {

List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());

for (SubscriberMethod subscriberMethod : subscriberMethods) {

subscribe(subscriber, subscriberMethod, sticky, priority);

}

}

通过for循环遍历List<SubscriberMethod>里的方法,同时传入suscribe方法。具体如下:

// Must be called in synchronized block

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {

//从订阅方法中拿到订阅事件的类型

Class<?> eventType = subscriberMethod.eventType;

通过订阅事件类型,找到所有的订阅(Subscription)

CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);

//创建一个新的订阅

Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);

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);

}

}

// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)

// subscriberMethod.method.setAccessible(true);

int size = subscriptions.size();

for (int i = 0; i <= size; i++) {

//根据优先级插入订阅

if (i == size || newSubscription.priority > subscriptions.get(i).priority) {

subscriptions.add(i, newSubscription);

break;

}

}

//根据subscriber存储它所有的eventType

List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);

if (subscribedEvents == null) {

subscribedEvents = new ArrayList<Class<?>>();

typesBySubscriber.put(subscriber, subscribedEvents);

}

//将这个订阅事件加入到订阅者的订阅事件列表中

subscribedEvents.add(eventType);

//判断sticky;如果为true,从stickyEvents中根据eventType去查找有没有stickyEvent,如果有则立即发布去执行。stickyEvent其实就是我们post时的参数

if (sticky) {

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).

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);

}

}

}

到这里register方法分析完了,大致流程总结一下:

  1. 找到被注册者类中的所有的订阅方法。

  2. 遍历订阅方法,找到EventBus中eventType对应的订阅列表,然后根据当前订阅者和订阅方法创建一个新的订阅加入到订阅列表。

  3. 找到EvnetBus中subscriber订阅的事件列表,将eventType加入到这个事件列表。

所以对于任何一个订阅者,我们可以找到它的订阅事件类型列表,通过这个订阅事件类型,可以找到在订阅者中的订阅函数。

既然register函数分析完了,那么接下来就该分析unregister了,成对出现嘛!如下:

/** Unregisters the given subscriber from all event classes. */

public synchronized void unregister(Object subscriber) {

List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);

if (subscribedTypes != null) {

for (Class<?> eventType : subscribedTypes) {

unubscribeByEventType(subscriber, eventType);

}

typesBySubscriber.remove(subscriber);

} else {

Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());

}

}

可以看到,首先获取了subscribe函数中根据subscriber存储它的所有eventType保存到List<Class<?>> subscribedTypes;接着如果存在注册过的type则通过unubscribeByEventType(subscriber, eventType);循环遍历,完事remove掉所有,这样就完成了所有的unregister功能;至于unubscribeByEventType函数如何实现,具体如下:

/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */

private void unubscribeByEventType(Object subscriber, Class<?> eventType) {

List 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–;

}

}

}

}

从上面代码可以看出,原来在register时真正存储EventBus事件的Map是subscriptionsByEventType成员。这里就是循环遍历找出需要unregister的remove掉。至此,整个EventBus的register与unregister函数都分析完毕。

依照前一篇使用来看,进行完register与unregister后剩下的就是post了,那么接下来分析分析post过程,如下:

/** Posts the given event to the event bus. */

public void post(Object event) {

PostingThreadState postingState = currentPostingThreadState.get();

List 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;

}

}

}

public void postSticky(Object event) {

synchronized (stickyEvents) {

stickyEvents.put(event.getClass(), event);

}

// Should be posted after it is putted, in case the subscriber wants to remove immediately

post(event);

}

如上postSticky(Object event)的实质是post了一个stickyEvents,而真正的post(Object event)方法里,currentPostingThreadState是一个ThreadLocal类型的,里面存储了PostingThreadState;PostingThreadState包含了一个eventQueue和一些标志位;eventQueue.add(event);就是把事件放入eventQueue队列,然后while循环遍历eventQueue通过postSingleEvent(eventQueue.remove(0), postingState);语句分发事件;那继续看下这条语句的实现:

private void postSingleEvent(Object event, PostingThreadState postingState) throw
s 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);

}

if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&

eventClass != SubscriberExceptionEvent.class) {

post(new NoSubscriberEvent(this, event));

}

}

}

如上代码通过List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);语句传入eventClass得到eventClass对应的事件,包含父类对应的事件和接口对应的事件;接着通过循环遍历eventTypes执行subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);语句;最后如果发现没有对应事件就通过post(new NoSubscriberEvent(this, event));post一个NoSubscriberEvent事件;接下来看下postSingleEventForEventType函数的实现:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {

CopyOnWriteArrayList subscriptions;

synchronized (this) {

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;

}

如上可以发现,我们在register时扫面class把匹配的方法都存储在了subscriptionsByEventType,这里通过subscriptions = subscriptionsByEventType.get(eventClass);语句首先拿到register时扫描的匹配方法;然后判断是否有匹配的方法,如果有就继续遍历每个subscription,依次去调用postToSubscription(subscription, event, postingState.isMainThread);

其实这个方法在register的subscribe的checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent)的方法中也调运过。所以我们继续来分析下这个方法,如下:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {

switch (subscription.subscriberMethod.threadMode) {

case PostThread:

invokeSubscriber(subscription, event);

break;

case MainThread:

if (isMainThread) {

invokeSubscriber(subscription, event);

} else {

mainThreadPoster.enqueue(subscription, event);

}

break;

case BackgroundThread:

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);

}

}

这个方法传入的三个参数含义分别是:第一个参数就是传入的订阅,第二个参数就是对于的分发事件,第三个参数表明是否在主线程;然后通过subscription.subscriberMethod.threadMode判断该在哪个线程去执行;这里通过switch分四种情况,如下:

  • case PostThread:直接在当前线程反射调用。

  • case MainThread:如果是(isMainThread)主UI线程则直接调用,否则把当前的方法加入到队列,然后直接通过handler去发送一个消息,通过Handler在主线程执行。

  • case BackgroundThread:如果当前不是主UI线程(!isMainThread)则直接调用,如果是UI线程则创建一个runnable加入到后台的一个队列,最终由Eventbus中的一个线程池去调用。

  • case Async:不论什么线程,直接丢入线程池,也就是将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。

  • default:抛出线程state状态非法异常。

继续分析可以发现mainThreadPoster是继承Handler实现的,其中Looper是MainLooper;invokeSubscriber与asyncPoster都是继承Runnable实现的,其中invokeSubscriber与asyncPoster的enqueue方法实质都差不多,如下:

public void enqueue(Subscription subscription, Object event) {

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

queue.enqueue(pendingPost);

eventBus.getExecutorService().execute(this);

}

可以验证上面说的,invokeSubscriber与asyncPoster的enqueue方法都是扔到了一个线程池中执行。好了,继续看下mainThreadPoster的enqueue方法,如下:

void enqueue(Subscription subscription, Object event) {

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

synchronized (this) {

queue.enqueue(pendingPost);

if (!handlerActive) {

ster的enqueue方法实质都差不多,如下:

public void enqueue(Subscription subscription, Object event) {

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

queue.enqueue(pendingPost);

eventBus.getExecutorService().execute(this);

}

可以验证上面说的,invokeSubscriber与asyncPoster的enqueue方法都是扔到了一个线程池中执行。好了,继续看下mainThreadPoster的enqueue方法,如下:

void enqueue(Subscription subscription, Object event) {

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

synchronized (this) {

queue.enqueue(pendingPost);

if (!handlerActive) {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值