【源码解读】EventBus3.0注册过程及事件分发源码分析

一、EventBus简介以及使用示例

引用官网的定义:

EventBus is a publish/subscribe event bus optimized for Android.

EventBus是针对Android优化的发布/订阅事件总线,主要功能是简化组件之间的消息传递。使用EventBus可以替代Handler、Intent、BroadCast在Activity、Fragment、Service和后台线程中的使用。具有开销小,代码优雅以及将发送者和接收者解耦等优点。其github网址为https://github.com/greenrobot/EventBus

EventBus的使用

EventBus的使用可以归结为4个步骤,下面通过结合代码来进行简单说明:

1.定义事件。这里的事件就是一个普通的Java类

class AEvent{
  //根据实际需求定义事件类的成员变量和方法
}

2.注册组件。以Activity为例,在onCreate()方法中注册,在onDestroy()中解注册。也可以在Fragment、Service中使用

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    EventBus.getDefault().register(this);
  }
  public void onDestroy() {
    super.onDestroy();
    EventBus.getDefault().unregister(this);
  }

3.订阅事件。在3.0之前EventBus提供了四种订阅事件的方法,onEvent,onEventMainThread,onEventBackground,onEventAsync。其区别简单来说就是区分响应事件的执行线程。在3.0后,通过 @Subscribe 注解我们可以自由的定义事件响应方法。下面是官方文档中的栗子:

// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
    doSomethingWith(event);
}

在上面的代码中通过threadMode = ThreadMode.MAIN指定了响应事件在主线程,3.0之后通过枚举类ThreadMode指定响应事件的线程,其代码如下

public enum ThreadMode {

    //默认该模式,响应事件直接在post事件的线程中执行,不适合执行耗时操作
    POSTING

    //在主线程(UI线程)中执行响应事件,不适合执行耗时操作,该模式对于Android来说非常有用
    MAIN,

    //如果是主线程分发的事件,那么响应事件会在子线程中执行。如果分发事件的是子线程,则会直接在该子线程中执行
    BACKGROUND,


    //无论在哪个线程分发事件,都会在新的子线程中执行。当响应事件中有耗时操作时应该选择该模式。EventBus使用线程池保证线程重用提高效率
    ASYNC

}

4.发送事件。通过post方法我们可以进行事件的发送,所有注册了发送事件的方法都会得到响应。

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

以上就是EventBus的使用步骤和简要代码示例,具体的用法已经有很多资料,这里不再赘述。

二、EventBus源码分析

之前学习EventBus也看了一些关于EventBus源码分析的文章,但是发现大部分文章发的较早,虽然原理不变但是在当前版本3.0.0中许多代码已经发生变化,因此在这里根据最新的3.0.0源码对其进行分析。

1.EventBus实例创建

在上面代码中我们都是通过EventBus.getDefault()方法来获取实例的,其源码如下,可以看出是通过典型的单例模式实现的,通过双重加锁既保证线程安全又保证了效率。

public static EventBus getDefault() {
     if (defaultInstance == null) {
         synchronized (EventBus.class) {
             if (defaultInstance == null) {
                 defaultInstance = new EventBus();
             }
         }
     }
     return defaultInstance;
 }
2.EventBus注册过程分析

我们通过register()方法来注册,在之前的文章中会告诉你有四个不同的注册方法,但在3.0.0中只有一个register方法,代码如下:

public void register(Object subscriber) {
      //获取到要注册组件也就是Activity、Fragment、Service等组件的Class对象
      Class<?> subscriberClass = subscriber.getClass();
      //通过Class对象查找订阅了该组件中的Event响应方法集合
      List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
      synchronized (this) {
          //遍历方法集合,调用subscribe对Event响应方法进行存储
          for (SubscriberMethod subscriberMethod : subscriberMethods) {
              subscribe(subscriber, subscriberMethod);
          }
      }
  }

从上面一段简单代码可以看出register过程主要是两步:
1. 调用findSubscriberMethods获取组件中的Event响应方法的集合;
2. 遍历集合调用subscribe()方法进行存储
接下来就看一下上面两步的实现过程

(1)获取方法集合
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
       //1.首先从缓存中获取方法集合,如果有直接返回
       List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
       if (subscriberMethods != null) {
           return subscriberMethods;
       }

      //下面两个方法都是调用findUsingReflectionInSingleClass获取集合的,我们直接看findUsingReflectionInSingleClass()方法
       if (ignoreGeneratedIndex) {
           subscriberMethods = findUsingReflection(subscriberClass);
       } else {
           subscriberMethods = findUsingInfo(subscriberClass);
       }
       //如果集合为空说明注册的事件中没有Event响应方法会抛出异常,如果有则进行缓存然后将集合返回
       if (subscriberMethods.isEmpty()) {
           throw new EventBusException("Subscriber " + subscriberClass
                   + " and its super classes have no public methods with the @Subscribe annotation");
       } else {
           METHOD_CACHE.put(subscriberClass, subscriberMethods);
           return subscriberMethods;
       }
   }

findUsingReflectionInSingleClass()代码(这里省略了一些无关紧要的异常抛出代码):

private void findUsingReflectionInSingleClass(FindState findState) {
     Method[] methods;
     try {
         // This is faster than getMethods, especially when subscribers are fat classes like Activities
         //获取组件中所有的方法,如官方文档注释的,该方法运行速度更快
         methods = findState.clazz.getDeclaredMethods();
     } catch (Throwable th) {
         // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
         methods = findState.clazz.getMethods();
         findState.skipSuperClasses = true;
     }
     //遍历组件方法
     for (Method method : methods) {
         //获取方法的修饰符
         int modifiers = method.getModifiers();
         //Event方法必须为public且不为static、abstract
         if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
             //获取方法的参数类型数组,这里Event响应方法只能有一个参数,这很容易理解,一个方法只能对应一个事件类  
             Class<?>[] parameterTypes = method.getParameterTypes();
             if (parameterTypes.length == 1) {
                //获取@Subscribe注解对象
                 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                 //注解不为null说明是Event响应方法
                 if (subscribeAnnotation != null){
                     //这里获取到监听的Event类的Class对象
                     Class<?> eventType = parameterTypes[0];
                     if (findState.checkAdd(method, eventType)) {
                         //获取注解标注的Event响应方法的执行线程
                         ThreadMode threadMode = subscribeAnnotation.threadMode();
                         //将订阅方法封装为SubscriberMethod对象
                         //SubscriberMethod对象封装了订阅方法的Method对象、执行线程、事件类型、优先级和粘性
                         //然后加入到FindState的subscriberMethods列表集合中,最终将该集合返回
                         findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                 subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                     }
                 }
             }             
         }
     }
 }

通过分析源码可以明确知道是通过遍历组件中所有的方法,然后将符合订阅规则的方法封装为SubscriberMethod对象进行存储。

(2)存储方法集合

上面大致分析了获取Event响应方法的过程,下面来分析subscribe方法看它是怎么对响应方法进行存储的:

  // Must be called in synchronized block必须在同步块中调用
   private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
       //这里获取的是Event方法所监听的Event类的Class对象
       Class<?> eventType = subscriberMethod.eventType;
       //将注册组件的Class对象和方法监听Event类的Class对象作为参数构建Subscription对象
       Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
       //根据监听的Event类型获取Subscription对象集合,如果集合为空则新建然后添加到subscriptionsByEventType集合中
       CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
       if (subscriptions == null) {
           subscriptions = new CopyOnWriteArrayList<>();
           subscriptionsByEventType.put(eventType, subscriptions);
       } else {
           //如果集合不为空且包含了该Subscription对象说明已经注册过了不能重复注册,抛出异常
           if (subscriptions.contains(newSubscription)) {
               throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                       + eventType);
           }
       }

       int size = subscriptions.size();
       //根据索引和优先级将新的Subscription加入到列表中
       for (int i = 0; i <= size; i++) {
           if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
               subscriptions.add(i, newSubscription);
               break;
           }
       }
       //通过注册组件对象获取到其注册的Event类型集合,然后将该Event类型加入到集合中
       List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
       if (subscribedEvents == null) {
           subscribedEvents = new ArrayList<>();
           typesBySubscriber.put(subscriber, subscribedEvents);
       }
       subscribedEvents.add(eventType);
       //粘性操作,这里不作分析
       if (subscriberMethod.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<Class>).
               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过程就算结束了。从结果来看,就是创建了两个Map集合:以注册的组件对象作为键,Event类型作为值的根据组件存储其监听事件类型的typesBySubscriber集合和以Event的Class为键,Subscription对象为值的存储Event的注册者和响应方法的subscriptionsByEventType集合。

3.EventBus分发事件流程分析

我们知道事件分发都是通过post方法开始的,下面我们看post的代码:

PostingThreadState封装了当前post线程的基本信息,通过ThreadLocal进行存储。ThreadLocal可以以线程作为键存储
数据,不熟悉的童鞋可以参考技术小黑屋的这篇文章:http://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/

public void post(Object event) {
        //获取到事件的队列集合
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> 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;
            }
        }
    }

上面的post方法,通过调用postSingleEvent()等一系列方法最终会调用到postToSubscription()根据ThreadMode信息对Event进行分发

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
       switch (subscription.subscriberMethod.threadMode) {
           //默认,直接在当前线程中执行
           case POSTING:
               invokeSubscriber(subscription, event);
               break;
           //如果是主线程,立即执行;如果不是,使用mainThreadPoster(就是一个Handler)切换到主线程执行
           case MAIN:
               if (isMainThread) {
                   invokeSubscriber(subscription, event);
               } else {
                   mainThreadPoster.enqueue(subscription, event);
               }
               break;
               //如果是主线程中,则使用backgroundPoster切换到子线程;如果是在子线程中立即执行
           case BACKGROUND:
               if (isMainThread) {
                   backgroundPoster.enqueue(subscription, event);
               } else {
                   invokeSubscriber(subscription, event);
               }
               break;
           case ASYNC://无论是否在主线程,直接使用asyncPoster切换到一个新的子线程中执行
               asyncPoster.enqueue(subscription, event);
               break;
           default:
               throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
       }
   }

   //不需要切换线程的直接调用invokeSubscriber()方法,本质上就是利用反射调用组件中的Event响应方法
   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);
       }
   }

上面对于不需要切换线程的方法调用很简单,我们不再赘述。接下来以MainThread为例研究下EventBus是如何切换线程的。
就主线程而言是通过mainThreadPoster对象来切换的,就是一个Handler类,下面是它的代码

final class HandlerPoster extends Handler {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }
    //调用mainThreadPoster的enqueue方法,首先将Subscription和Event对象封装为PendingPost对象然后存储进
    //PendingPostQueue待分发事件的队列中,然后发送Message调用handleMessage作处理
    void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {//无限循环遍历待分发事件
                //获取分发事件集合的第一个事件
                PendingPost pendingPost = queue.poll();
                //加锁双重判空操作,确保队列中没有事件时不作任何处理
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        //如果为空在判断一次如果还未空直接返回不作任何处理
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                //如果不为空则调用invokeSubscriber方法通过反射调用方法
                //最终还是通过invokeSubscriber(Subscription subscription, Object event)调用方法
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {//如果超时则再次发送消息进行调用
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

简而言之,就是根据ThreadMode调用相应的Handler类来进行线程的切换,然后通过反射执行Event响应方法。
上面就是整个EventBus的注册和post流程。这里画了两张图表示注册和分发过程,希望对需要的童鞋有所帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值