- 反射调用
前面简单聊过了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
存储的是,注册对象subscriberMethodFinder
和Subscribe
标记方法
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
就是具体实例对象,具体方法,对应用例中stringEventSubscriber
,event
存储下,注册对象,这样就可以根据注册对象,知道具体是哪个对象,哪个方法需要调用了。
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也是可以接收到的,例如下面这种场景,onEvent1
,onEvent2
都可以接收到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);
}
}
看一下代码,主要分析两个,mainThreadPoster
和backgroundPoster
,顾名思义。
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;
}
}
}