由Handler到EventBus

625 阅读4分钟
  • 反射调用

前面简单聊过了Android原生的Handler消息机制,在聊EventBus之前,先看个问题

如何通过反射调用某个对象的方法,比如下面这个event方法

public class MessageEvent {
    public void event(String data){
        Log.d("event", "data:" + data);
    }
}

在不使用反射的时候,通常是这样可以实现

  MessageEvent event = new MessageEvent();
  event.event("AAAA");

通过反射可以这样处理

MessageEvent event = new MessageEvent();
event.getClass().getMethod("event", String.class).invoke(event,"AAAA");

得到结果是一样的,EventBus中主要通过反射对订阅者的注解方法进行调用,也就是后者的实现方式。

subscription.subscriberMethod.method.invoke(subscription.subscriber, event);

  • 订阅/注册

EventBus 注册使用就是调用register方法,传入一个实例对象,它的思路是通过该注册对象,去找到该对象对应类中注解Subscribe方法,缓存起来,下面是注册用例。为了方便理解,后面会结合这个用例分析代码。

  • 注册对象 stringEventSubscriber
  • 注册对象对应类 StringEventSubscriber
  • 需要关注的注解标记Subscribe
StringEventSubscriber stringEventSubscriber = new StringEventSubscriber();
String event = "Hello";
eventBus.register(stringEventSubscriber);

public static class StringEventSubscriber {
    public String lastStringEvent;

    @Subscribe
    public void onEvent(String event) {
        lastStringEvent = event;
    }
}

继续看一下注册源代码

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

根据注册对象对应类StringEventSubscriber,查找带有Subscribe这个标记的注解方法

private void findUsingReflectionInSingleClass(FindState findState) {
    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()));
        }
    }
}

建立绑定关系,缓存下数据,Subscription存储的是,注册对象subscriberMethodFinderSubscribe标记方法

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    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);
}

subscriptionsByEventType.put(eventType, subscriptions);

eventType 对应用例中是String

subscriptions 就是具体实例对象,具体方法,对应用例中stringEventSubscriberevent

存储下,注册对象,这样就可以根据注册对象,知道具体是哪个对象,哪个方法需要调用了。

typesBySubscriber.put(subscriber, subscribedEvents);

  • 通知/发送事件/广播数据
public void post(Object event) {
    ...
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    ...
    while (!eventQueue.isEmpty()) {
        postSingleEvent(eventQueue.remove(0), postingState);
    }
    ...
}

PostingThreadState postingState = currentPostingThreadState.get();

List<Object> eventQueue = postingState.eventQueue;

从当前线程中,获取到绑定对象,这个跟Handler消息机制中的Looper,效果差不多;从队列中循环取出消息;

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    ...
    postToSubscription(subscription, event, postingState.isMainThread);
    ...
    return true;
    }
    return false;
}

subscriptions = subscriptionsByEventType.get(eventClass);

从注册缓存的数据,取出注册对象信息,通过反射调用,这样就完成了事件通知。

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

subscription.subscriberMethod.method.invoke(subscription.subscriber, event);这个就是开头我介绍的反射调用,调用一个对象的某个方法。

  • 事件继承性

就是在发送事件的时候,不仅仅是发送对象当前Class类型,还会去查找它对应的父类和接口Class类型。

 /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                addInterfaces(eventTypes, clazz.getInterfaces());
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
    for (Class<?> interfaceClass : interfaces) {
        if (!eventTypes.contains(interfaceClass)) {
            eventTypes.add(interfaceClass);
            addInterfaces(eventTypes, interfaceClass.getInterfaces());
        }
    }
}

所以如果你发送的是一个String类型的数据,Object也是可以接收到的,例如下面这种场景,onEvent1onEvent2都可以接收到hello这个消息。

eventBus.post("Hello");

@Subscribe
public void onEvent1(Object event) {
    countObjectEvent++;
}

@Subscribe
public void onEvent2(String event) {
    countObjectEvent++;
}

这个特征主要是通过一个变量eventInheritance控制的,默认是开启的,当然也可以在创建EventBus的时候把它禁止掉EventBus.builder().eventInheritance(false).build()

  • 线程切换

通过注解 @Subscribe(threadMode = ThreadMode.BACKGROUND) @Subscribe(threadMode = ThreadMode.MAIN) 支持消息接收的时候,自动切换到指定的线程。不管post消息的线程是UI线程,还是子线程,最后接收消息,EventBus都可以根据不同注解,切换线程,再反射调用。

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

看一下代码,主要分析两个,mainThreadPosterbackgroundPoster,顾名思义。

mainThreadPoster应该就是发送到UI线程了,backgroundPoster就是发送到后台子线程了。mainThreadPoster只不过是对Handler封装了一下,内部主要是拿到了UI线程的Handler,

@Override
public Poster createPoster(EventBus eventBus) {
    return new HandlerPoster(eventBus, Looper.getMainLooper(), 10);
}

backgroundPoster 是通过后台线程池去执行

public void enqueue(Subscription subscription, Object event) {
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    synchronized (this) {
        queue.enqueue(pendingPost);
        if (!executorRunning) {
            executorRunning = true;
            eventBus.getExecutorService().execute(this);
        }
    }
}

最后还是通过EventBus 内部方法,反射调用

eventBus.invokeSubscriber(pendingPost);

  • 索引代替搜索

看过源码我们知道EventBus 在订阅过程中,大量的反射搜索过程。为了解决这个耗时操作,后面EventBus通过EventBusAnnotationProcessor自动生成了索引。其实就是编译期间提前把搜索逻辑生成到辅助类。

public class EventBusTestsIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
        ...
        putIndex(new SimpleSubscriberInfo(EventBusAndroidMultithreadedTest.SubscribeUnsubscribeThread.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventMainThread", String.class, ThreadMode.MAIN),
            new SubscriberMethodInfo("onEventBackgroundThread", Integer.class, ThreadMode.BACKGROUND),
            new SubscriberMethodInfo("onEvent", Object.class),
            new SubscriberMethodInfo("onEventAsync", Object.class, ThreadMode.ASYNC),
        }));
        ...

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}