源码阅读计划 - EventBus
2021.08.12
林嘉伟
EventBus的api很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EventBus.getDefault().post(new MyEvent(123)); } }); }
@Override protected void onResume() { super.onResume(); EventBus.getDefault().register(this); }
@Override protected void onPause() { super.onPause(); EventBus.getDefault().unregister(this); }
@Subscribe(threadMode = ThreadMode.MAIN) public void onMyEvent(MyEvent event) { Log.d("testtest", "onMyEvent " + event.data); } }
class MyEvent { public int data;
MyEvent(int data) { this.data = data; } }
|
注册监听的原理
坦白讲内部的实现原理也挺简单的,我们从注册开始看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class<?> eventType = subscriberMethod.eventType; Subscription newSubscription = new Subscription(subscriber, subscriberMethod); CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(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; } } ... }
|
使用Finder从对象的Class中查找注册的回调方法的信息,使用Subscription将调用者与方法信息绑定起来,然后使用Event的Class作为key将它放到subscriptionsByEventType这个map中。由于同一种Event可能会有多个观察者,所以map的value是一个按优先级排序的List。
SubscriberMethod和Subscription的定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class SubscriberMethod { final Method method; final ThreadMode threadMode; final Class<?> eventType; final int priority; final boolean sticky; ... }
final class Subscription { final Object subscriber; final SubscriberMethod subscriberMethod; ... }
|
从上面可以大概猜出来,事件的分发实际是反射调用的方法。
也就是说当我们使用post方法发送Event的时候就能用Event的Class查找到所有的Subscription,通过Subscription的subscriber找到注册的对象、subscriberMethod找到注册的方法,接着就能使用反射去进行分发。
整个注册的流程大体上是比较清晰的,但是findSubscriberMethods的查找注册信息流程中有些小的细节也比较值得学习。
1.缓存
第一点是扫描完一个class之后会将监听的信息放入METHOD_CACHE缓存中,例如我们的demo,onResume的时候register,onPause的时候unregister,就能减少第二次onResume的耗时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } ... subscriberMethods = findUsingInfo(subscriberClass); ... METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; ... }
|
2.APT生成索引
第二点就是由于反射遍历类方法去查找被@Subscribe修饰的方法比较耗时,所以可以使用apt编译时根据注解生成代码的方式直接生成索引:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { ... } else { findUsingReflectionInSingleClass(findState); } findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
|
apt创建索引的使用方法见官方文档
3.对象池减少内存碎片
第三就是FindState是一个辅助查找的工具类,为了避免应用初始化的时候多个类都在注册到EventBus,导致这个FindState创建消耗多次,产生内存碎片,所以它使用类对象池技术:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| private FindState prepareFindState() { synchronized (FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { FindState state = FIND_STATE_POOL[i]; if (state != null) { FIND_STATE_POOL[i] = null; return state; } } } return new FindState(); }
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) { List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods); findState.recycle(); synchronized (FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { if (FIND_STATE_POOL[i] == null) { FIND_STATE_POOL[i] = findState; break; } } } return subscriberMethods; }
|
消息分发原理
上面我们看完了注册,实际上分发的流程大概也能猜出来了。不过这里面还是有蛮多小细节同样值得学习的。
1.Event Queue
EventBus的Event分发并不是直接循环遍历观察者进行分发,而是先将Event放到队列中,然后再去队列里面拿出来分发。
PostingThreadState使用了ThreadLocal,每条线程都有自己的Event队列,而且isPosting能够记录当前的线程是否正在分发Event。
也就是说如果在Event的回调中再去post一个Event,并不会立刻分发,而是会等之前的Event都分发完之后在去分发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }
|
这里有个小坑:threadMode是POSTING,只能代表回调是在post的线程调用的,并不能代表post方法里面会立即调用
2.eventInheritance
当一个观察者监听的是父类,如果post了一个子类的Event,默认情况下观察者是可以接收到这个子类的Event。这是因为默认情况下EventBusBuilder.eventInheritance为true:
1
| boolean eventInheritance = true;
|
如果不想要这个功能我们可以设置成false。它的原理在postSingleEvent里面可以看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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); } ... }
|
postSingleEventForEventType方法就是根据Class查找register的对应Subscription,调用postToSubscription进行实际的分发操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { subscriptions = subscriptionsByEventType.get(eventClass); } if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { ... postToSubscription(subscription, event, postingState.isMainThread); ... } return true; } return false; }
|
3.threadMode原理
postToSubscription内部就会根据不同的threadMode在不同线程使用反射调用注册的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case POSTING: .. break; case MAIN: ... break; case MAIN_ORDERED: ... break; case BACKGROUND: ... break; case ASYNC: ... break; default: ... } }
|