EventBus 使用方法及源码分析

ゝ一纸荒年。 2022-05-29 14:12 289阅读 0赞

EventBus 使用方法及源码分析

标签(空格分隔): android


#基本使用方法

  1. **订阅模式**
  2. POSTING:发布跟订阅在同一线程,开销最小,默认的模式;
  3. MAIN
  4. 如果发布者在非ui线程,订阅者会切换到ui线程;
  5. MAIN_ORDERED
  6. 订阅者会先放到队列里,直到执行完相关逻辑代码才会处理订阅的代码。如果发布者在非ui线程,订阅者会切换到ui线程;
  7. BACKGROUND
  8. 如果发布者在ui线程,订阅者会另开一个线程;如果发布者在非ui线程,订阅者会在当前线程;
  9. ASYNC
  10. 订阅者都会另开一个线程
  11. Sticky 默认为false
  12. Sticky=true
  13. Sticky事件只指事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型。
  14. **订阅事件**
  15. EventBus.getDefault().register(this); // 订阅
  16. **订阅回调方法**
  17. @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) // sticky事件
  18. public void hand(Event01 event01) {
  19. String name = event01.getName();
  20. }
  21. **发布事件**
  22. EventBus.getDefault().postSticky(new Event01("jack")); // 发布sticky事件
  23. EventBus.getDefault().post(new Event01("jack")); // 发布非sticky事件

#源码分析

##订阅流程的源码分析

  1. public static EventBus getDefault() {
  2. if (defaultInstance == null) {
  3. synchronized (EventBus.class) {
  4. if (defaultInstance == null) {
  5. defaultInstance = new EventBus();
  6. }
  7. }
  8. }
  9. return defaultInstance;
  10. }
  11. public void register(Object subscriber) {
  12. Class<?> subscriberClass = subscriber.getClass();
  13. List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
  14. synchronized (this) {
  15. for (SubscriberMethod subscriberMethod : subscriberMethods) {
  16. subscribe(subscriber, subscriberMethod);
  17. }
  18. }
  19. }

1.通过单例模式EventBus.getDefault()得到实例eventBus, 然后通过eventBus.register(Object subscriber) 方法实现订阅;

2.在register方法里, 首先会通过实例得到相应的类, 然后通过(该实例是在EventBus(EventBusBuilder builder )构造方法里实例化的)subscriberMethodFinder.findSubscriberMethods(subscriberClass) 得到订阅者的该类中所有订阅方法;

org.greenrobot.eventbus.SubscriberMethodFinder 类:

  1. List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
  2. List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
  3. if (subscriberMethods != null) {
  4. return subscriberMethods;
  5. }
  6. if (ignoreGeneratedIndex) {
  7. subscriberMethods = findUsingReflection(subscriberClass);
  8. } else {
  9. subscriberMethods = findUsingInfo(subscriberClass);
  10. }
  11. if (subscriberMethods.isEmpty()) {
  12. throw new EventBusException("Subscriber " + subscriberClass
  13. + " and its super classes have no public methods with the @Subscribe annotation");
  14. } else {
  15. METHOD_CACHE.put(subscriberClass, subscriberMethods); // 再将得到的订阅者方法保存,方便用
  16. return subscriberMethods;
  17. }
  18. }

3.该方法里ConcurrentHashMap(METHOD_CACHE实现缓存)保存类的所有订阅者方法,如果缓存中没有相应的信息,ignoreGeneratedIndex(boolean默认是false)则调用findUsingInfo(subscriberClass),得到了相应的信息会保存METHOD_CACHE方便下次用;

org.greenrobot.eventbus.SubscriberMethodFinder 类:

  1. private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
  2. FindState findState = prepareFindState();
  3. findState.initForSubscriber(subscriberClass);
  4. while (findState.clazz != null) {
  5. findState.subscriberInfo = getSubscriberInfo(findState);
  6. if (findState.subscriberInfo != null) {
  7. SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
  8. for (SubscriberMethod subscriberMethod : array) {
  9. if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
  10. findState.subscriberMethods.add(subscriberMethod);
  11. }
  12. }
  13. } else {
  14. findUsingReflectionInSingleClass(findState);
  15. }
  16. findState.moveToSuperclass();
  17. }
  18. return getMethodsAndRelease(findState);
  19. }

4.该方法里把相关信息保存在FindState实例(通过prepareFindState()该方法里通过FIND_STATE_POOL数组进行stateFind的缓存如果数组里面有stateFind则直接方法并把数组相应的位置的保存对象设为null, 否则就new一个)里面,

org.greenrobot.eventbus.SubscriberMethodFinder 类:

  1. private void findUsingReflectionInSingleClass(FindState findState) {
  2. Method[] methods;
  3. try {
  4. // This is faster than getMethods, especially when subscribers are fat classes like Activities
  5. methods = findState.clazz.getDeclaredMethods();
  6. } catch (Throwable th) {
  7. // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
  8. methods = findState.clazz.getMethods();
  9. findState.skipSuperClasses = true;
  10. }
  11. for (Method method : methods) {
  12. int modifiers = method.getModifiers();
  13. if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
  14. Class<?>[] parameterTypes = method.getParameterTypes();
  15. if (parameterTypes.length == 1) {
  16. Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
  17. if (subscribeAnnotation != null) {
  18. Class<?> eventType = parameterTypes[0];
  19. if (findState.checkAdd(method, eventType)) {
  20. ThreadMode threadMode = subscribeAnnotation.threadMode();
  21. findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); // 保存到findState对象里
  22. }
  23. }
  24. } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
  25. String methodName = method.getDeclaringClass().getName() + "." + method.getName();
  26. throw new EventBusException("@Subscribe method " + methodName +
  27. "must have exactly 1 parameter but has " + parameterTypes.length);
  28. }
  29. } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
  30. String methodName = method.getDeclaringClass().getName() + "." + method.getName();
  31. throw new EventBusException(methodName +
  32. " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
  33. }
  34. }
  35. }

5.通过findUsingReflectionInSingleClass(FindState findState)该方法里通过反射得到所有DeclaredMethod,
然后遍历所有的方法,通过判断方法的参数个数为1,然后再获取方法注解类是否为Subscribe,判断通过后stateFind.checkAdd(Method method, Class<?> eventType)添加到stateFind实例的anyMethodByEventType
hashMap里,该方法会检查是否有存在多个订阅者监听相同的事件类型,checkAdd返回true,最后会将注解类里信息保存到SubscriberMethod实例,添加到stateFind的subscriberMethods集合里面;

org.greenrobot.eventbus.SubscriberMethodFinder 类:

  1. private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
  2. List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
  3. findState.recycle();
  4. synchronized (FIND_STATE_POOL) {
  5. for (int i = 0; i < POOL_SIZE; i++) {
  6. if (FIND_STATE_POOL[i] == null) {
  7. FIND_STATE_POOL[i] = findState;
  8. break;
  9. }
  10. }
  11. }
  12. return subscriberMethods;
  13. }

6.由1-5 得到了一些订阅者的信息保存在stateFind实例里,findUsingInfo方法最后通过getMethodsAndRelease(findState)得到List,然后将stateFind实例的属性进行回收, 再将stateFind保存到FIND_STATE_POOL(步骤4有去缓存拿的操作) 数组里面方便下次再用;

  1. 7. 1-6 eventBus.register(Object subscriber)方法得到了List<SubscriberMethod>后再遍历调用subscribe(subscriber, subscriberMethod);

org.greenrobot.eventbus.EventBus;类

  1. private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
  2. Class<?> eventType = subscriberMethod.eventType;
  3. Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
  4. CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
  5. if (subscriptions == null) {
  6. subscriptions = new CopyOnWriteArrayList<>();
  7. subscriptionsByEventType.put(eventType, subscriptions);
  8. } else {
  9. if (subscriptions.contains(newSubscription)) {
  10. throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); // 同一个订阅者不能订阅2个相同的事件类型
  11. }
  12. }
  13. int size = subscriptions.size();
  14. for (int i = 0; i <= size; i++) {
  15. if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
  16. subscriptions.add(i, newSubscription);
  17. break;
  18. }
  19. }
  20. List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
  21. if (subscribedEvents == null) {
  22. subscribedEvents = new ArrayList<>();
  23. typesBySubscriber.put(subscriber, subscribedEvents);
  24. }
  25. subscribedEvents.add(eventType);
  26. if (subscriberMethod.sticky) { // sticky 事件的处理
  27. if (eventInheritance) {
  28. // Existing sticky events of all subclasses of eventType have to be considered.
  29. // Note: Iterating over all events may be inefficient with lots of sticky events,
  30. // thus data structure should be changed to allow a more efficient lookup
  31. // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
  32. Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
  33. for (Map.Entry<Class<?>, Object> entry : entries) {
  34. Class<?> candidateEventType = entry.getKey();
  35. if (eventType.isAssignableFrom(candidateEventType)) {
  36. Object stickyEvent = entry.getValue();
  37. checkPostStickyEventToSubscription(newSubscription, stickyEvent);
  38. }
  39. }
  40. } else {
  41. Object stickyEvent = stickyEvents.get(eventType);
  42. checkPostStickyEventToSubscription(newSubscription, stickyEvent);
  43. }
  44. }
  45. }

8.subscribe方法对非Sticky事件总要管理2个集合,subscriptionsByEventType (Map eventType, CopyOnWriteArrayList>)(key是事件类型,value是一个集合,Subscription实例保存subscriber订阅者,subscriberMethod订阅者里面的订阅方法), typesBySubscriber(Map

##发布事件流程的源码分析

  1. EventBus.getDefault().post(mainEvent); 发布一个事件类型为MainEvent的事件, post(Object event)方法如下:PostingThreadState类型的对象里面存有一个事件队列,isMainThread是否是主线程,isPosting是否发送, canceled是否已经取消发送;

org.greenrobot.eventbus.EventBus;类

  1. public void post(Object event) {
  2. PostingThreadState postingState = currentPostingThreadState.get(); // ThreadLocal保存的线程私有对象
  3. List<Object> eventQueue = postingState.eventQueue;
  4. eventQueue.add(event); // 将该事件类型加到队列
  5. if (!postingState.isPosting) {
  6. postingState.isMainThread = isMainThread();
  7. postingState.isPosting = true;
  8. if (postingState.canceled) {
  9. throw new EventBusException("Internal error. Abort state was not reset");
  10. }
  11. try {
  12. while (!eventQueue.isEmpty()) {
  13. postSingleEvent(eventQueue.remove(0), postingState);
  14. }
  15. } finally {
  16. postingState.isPosting = false;
  17. postingState.isMainThread = false;
  18. }
  19. }
  20. }

2.如果postingThreadState里面的事件队列(先进先出)不为空会一直循环执行postSingleEvent方法, 该方法首先lookupAllEventTypes里面通过一个集合缓存事件类型类(包括父类),得到事件类型的类然后遍历执行postSingleEventForEventType方法;

org.greenrobot.eventbus.EventBus;类

  1. private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
  2. Class<?> eventClass = event.getClass();
  3. boolean subscriptionFound = false;
  4. if (eventInheritance) {
  5. List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
  6. int countTypes = eventTypes.size();
  7. for (int h = 0; h < countTypes; h++) {
  8. Class<?> clazz = eventTypes.get(h);
  9. subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
  10. }
  11. } else {
  12. subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
  13. }
  14. if (!subscriptionFound) {
  15. if (logNoSubscriberMessages) {
  16. logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
  17. }
  18. if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
  19. eventClass != SubscriberExceptionEvent.class) {
  20. post(new NoSubscriberEvent(this, event));
  21. }
  22. }
  23. }
  24. private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
  25. synchronized (eventTypesCache) {
  26. List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
  27. if (eventTypes == null) {
  28. eventTypes = new ArrayList<>();
  29. Class<?> clazz = eventClass;
  30. while (clazz != null) {
  31. eventTypes.add(clazz);
  32. addInterfaces(eventTypes, clazz.getInterfaces());
  33. clazz = clazz.getSuperclass();
  34. }
  35. eventTypesCache.put(eventClass, eventTypes);
  36. }
  37. return eventTypes;
  38. }
  39. }

3.该方法首先会通过事件类型的类去subscriptionsByEventType集合里面找到List集合(订阅事件的流程8有对该集合的保存操作),然后遍历集合将一些数据保存在线程私有的变量postingState里面,执行postToSubscription方法,我们这里订阅类型是Main, 当前执行的线程是主线程,所以执行invokeSubscriber(subscription, event);该方法是通过反射执行订阅者实例里面的订阅方法。

  1. private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
  2. CopyOnWriteArrayList<Subscription> subscriptions;
  3. synchronized (this) {
  4. subscriptions = subscriptionsByEventType.get(eventClass);
  5. }
  6. if (subscriptions != null && !subscriptions.isEmpty()) {
  7. for (Subscription subscription : subscriptions) {
  8. postingState.event = event;
  9. postingState.subscription = subscription;
  10. boolean aborted = false;
  11. try {
  12. postToSubscription(subscription, event, postingState.isMainThread);
  13. aborted = postingState.canceled;
  14. } finally {
  15. postingState.event = null;
  16. postingState.subscription = null;
  17. postingState.canceled = false;
  18. }
  19. if (aborted) {
  20. break;
  21. }
  22. }
  23. return true;
  24. }
  25. return false;
  26. }
  27. private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
  28. switch (subscription.subscriberMethod.threadMode) {
  29. case POSTING:
  30. invokeSubscriber(subscription, event);
  31. break;
  32. case MAIN:
  33. if (isMainThread) {
  34. invokeSubscriber(subscription, event);
  35. } else {
  36. mainThreadPoster.enqueue(subscription, event);
  37. }
  38. break;
  39. case MAIN_ORDERED:
  40. if (mainThreadPoster != null) {
  41. mainThreadPoster.enqueue(subscription, event);
  42. } else {
  43. // temporary: technically not correct as poster not decoupled from subscriber
  44. invokeSubscriber(subscription, event);
  45. }
  46. break;
  47. case BACKGROUND:
  48. if (isMainThread) {
  49. backgroundPoster.enqueue(subscription, event);
  50. } else {
  51. invokeSubscriber(subscription, event);
  52. }
  53. break;
  54. case ASYNC:
  55. asyncPoster.enqueue(subscription, event);
  56. break;
  57. default:
  58. throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
  59. }
  60. }
  61. void invokeSubscriber(Subscription subscription, Object event) {
  62. try {
  63. subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
  64. } catch (InvocationTargetException e) {
  65. handleSubscriberException(subscription, event, e.getCause());
  66. } catch (IllegalAccessException e) {
  67. throw new IllegalStateException("Unexpected exception", e);
  68. }
  69. }

##疑问解答

  1. eventBus是怎么判断主线程的?

通过Logger.AndroidLogger.isAndroidLogAvailable()得到log可用,然后通过getAndroidMainLooperOrNull方法得到Looper.getMainLooper()对象,再用该对象判断是否跟Looper.myLooper()是同一对象来得知是否是主线程。

  1. 用到什么数据结构?
    队列
  2. 用到了什么模式?
    单例(双重检查)模式

发表评论

表情:
评论列表 (有 0 条评论,289人围观)

还没有评论,来说两句吧...

相关阅读

    相关 EventBus分析

    前言 EventBus是一种用于Android的发布/订阅事件总线。它有很多优点:简化应用组件间的通信;解耦事件的发送者和接收者;避免复杂和容易出错的依赖和生命周期的问题