Android EventBus源码流程解析

Posted 涂程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android EventBus源码流程解析相关的知识,希望对你有一定的参考价值。

1. 使用详解

  1. 注册(最好在activity或者fragment中的onStart()方法以及onStop方法中注册)
  2. 解绑,在对应的生命周期方法中。
  3. 发送消息
  4. 接收消息
    • ThreadMode.MAIN 表示这个方法在主线程中执行。事件的处理会在UI线程中执行。事件处理时间不能太长,长了会ANR的。
    • ThreadMode.BACKGROUND表示该方法在后台执行,不能并发处理.如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
    • ThreadMode.ASYNC,表示在后台执行,可以异步并发处理.无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作。
    • ThreadMode.POSTING,表示该方法和消息发送方在同一个线程中执行。如果使用事件处理函数指定了线程模型为POSTING,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。首先,需要新建一个类,作为发送消息的对象
public final String message;

    public MessageEvent(String message) 
        this.message = message;
    

其次,在需要发送消息的activity中或者fragment中

  EventBus.getDefault().post(new MessageEvent("这是使用EventBus进行通信的"));

最后在接受消息的activity或者fragment中注册EventBus,并且设置接受消息的方法。首先需要注册以及取消EventBus时间

 @Override
    public void onStart() 
        super.onStart();
        EventBus.getDefault().register(this);

    

     @Override
    public void onDestroy() 
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void getMsg(MessageEvent messageEvent)
        textView.setText(messageEvent.message);
    

2.EventBus3.0粘性事件

在MainActivity中新写一个方法用来处理粘性事件:

@Subscribe(threadMode = ThreadMode.POSTING,sticky = true)
    public void ononMoonStickyEvent(MessageEvent messageEvent)
        tv_message.setText(messageEvent.getMessage());
       // 如果在接收后,从stickEvents中移除粘性事件,可以这样:
        if (messageEvent!= null) 
            EventBus.getDefault().removeStickyEvent(messageEvent);
        
    

发送粘性事件
在SecondActivity中我们定义一个Button来发送粘性事件:

 bt_subscription.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                EventBus.getDefault().postSticky(new MessageEvent("粘性事件"));
                finish();
            
        );

2. 源码解析

register

|---EventBus
|   |---register(Object subscriber)
|   |   |--- subscriberMethodFinder.findSubscriberMethods(subscriberClass)
|   |   |---subscribe(subscriber, subscriberMethod);
|---findUsingReflectionInSingleClass
|   |--- Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);//找到带有注解的方法
|   |---SubscriberMethod() // 找到方法后,并将属性封装进这个对象。
  1. EventBus的原理是观察者模式,所以,首先需要有的是注册订阅者,其次是发送事件。
  2. 注册订阅者用到的就是register方法。 EventBus.getDefault().register(this); 首先分析getDefault(),通过它获取EventBus的单例,(实现方法是双重锁方法),内部还有个builder,使用了builder设计模式。然后进入到register方法
 public void register(Object subscriber) 
        Class<?> subscriberClass = subscriber.getClass(); //获取到订阅者类(Activity/Fragment)
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //通过一个subscribeMethod用去来装。
        synchronized (this) 
            for (SubscriberMethod subscriberMethod : subscriberMethods) 
                subscribe(subscriber, subscriberMethod);
            
        
    

 // 🌟 SubscriberMethodFinder
 //    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) 
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) 
            return subscriberMethods;
        

        if (ignoreGeneratedIndex) 
          // 🌟 
            subscriberMethods = findUsingReflection(subscriberClass);
         else 
            subscriberMethods = findUsingInfo(subscriberClass);
        
        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;
        
       

 private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) 
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) 
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        
        return getMethodsAndRelease(findState);
    
// 🌟
    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();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) 
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) 
                    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()));
                        
                    
                 else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) 
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                
             else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) 
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            
        
    
  1. SubscriberMethod()
new SubscriberMethod(method, // 方法名
eventType, // 方法参数
threadMode, // 注解中的信息
subscribeAnnotation.priority(),
subscribeAnnotation.sticky())
  1. findUsingReflectionInSingleClass

去解析注册者对象的所有方法。并且拿到带有注解的方法。 判断含有注解的方法是否是public,判断带有注解的方法的变量是否只有一个.然后通过annotation解析所有细节参数,包括ThreadMode,Priority,sticky,参数的类型。吧这些参数封装成一个对象SubscriberMethod

重点:

在获取完subscriberMethods之后,在register中进行遍历

  synchronized (this) 
            for (SubscriberMethod subscriberMethod : subscriberMethods) 
                subscribe(subscriber, subscriberMethod);
            
        

然后绑定关系

 // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) 
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // 将注册的类以及sbcribeMethod绑定
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) 
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);// 将parmeter类型与subcriptions绑定
         else 
            if (subscriptions.contains(newSubscription)) 
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + 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;
            
        

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

    private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) 
        if (stickyEvent != null) 
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        
    

postToSubscription

 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) 
 // 根据不同的threadMode 反射执行 invokeSubscriber
        switch (subscription.subscriberMethod.threadMode) 
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) 
            // 🌟 1、如果已经处于UI线程,则直接调用
            invokeSubscriber(subscription, event);
         else 
            //🌟 2、如果非UI线程,则调用mainThreadPoster处理
            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);
        
    

    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);
        
    
  • 如果是posting,直接调用invokeSubscriber,如果是MAIN,则需要判断发送事件的线程是否是主线程,如果是,直接调用invokeSubscriber,如果不是,需要向mainThreadPoster中enqueue(),
  • mainThreadPoster实际上是一个handler

HandlerPoster

new HandlerPoster(eventBus, looper, 10)
//🌟 本身是一个Handler,也是一个Post接口的实现类
public class HandlerPoster extends Handler implements Poster 

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

    protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) 
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    

    public void enqueue(Subscription subscription, Object event) 
       //将subscription和event构建成一个PendingPost对象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription以上是关于Android EventBus源码流程解析的主要内容,如果未能解决你的问题,请参考以下文章

EventBus3.0源码解析

源码解析-EventBus

EventBus 3.0源码解析

EventBusEventBus 源码解析 ( 注册订阅者总结 | EventBus 注册订阅者流程梳理 )

Android EventBus源码解析, 带你深入理解EventBus

Android EventBus3.0使用及源码解析