乐趣区

关于android:EventBus-30-源码分析

EventBus 3.0 源码剖析

简介

EvenntBus 是一个开源库,它利用公布 / 订阅者者模式来对我的项目进行解耦。它能够利用很少的代码,来实现多组件间通信。android 的组件间通信,咱们不由得会想到 handler 音讯机制和播送机制,通过它们也能够进行通信,然而应用它们进行通信,代码量多,组件间容易产生耦合援用。对于 EventBus 的工作模式,这里援用一张官网图帮忙了解。

为什么会抉择应用 EventBus 来做通信?

  • 简化了组件间交换的形式
  • 对事件通信单方进行解耦
  • 能够灵便不便的指定工作线程,通过 ThreadMode
  • 速度快,性能好
  • 库比拟小,60k 左右,对包大小无影响
  • 应用这个库的 app 多,有权威性
  • 性能多,使用方便

EventBus 的应用也非常简单,三板斧。register,unregister, subscribe/post

三个重要的角色

1:Publisher 事件发布者

2:Subscriber 事件订阅者

3:Event 事件

Publisher post 事件后,Subscriber 会主动收到事件 (订阅办法会被被动调用,并将事件传递过去)。

应用

 // 倒入 gradle 依赖
 implementation 'org.greenrobot:eventbus:3.3.1'

1: 定义事件类型

public static class MessageEvent {/* Additional fields if needed */}

2: 在须要订阅事件的模块中注册 EventBus,页面销毁时留神登记

 @Override
 protected void onStart() {super.onStart();
    EventBus.getDefault().register(this);    
 }

@Override
protected void onDestroy() {super.onDestroy();
    EventBus.getDefault().unregister(this);
}

3: 注册须要承受的事件类型 // 留神同一种事件类型不能反复注册。不然会解体,且订阅办法必须是 public 类型的。

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {// Do something}

4. 发送事件

EventBus.getDefault().post(new MessageEvent());
这是步骤 3 种的办法就会收到 MessageEvent 事件的回调 

源码剖析

EventBus 主类中只有不到 600 行代码,十分精简。EventBus 应用了对外提供了单例模型,外部构建应用了 Build 模式。

1 register

public void register(Object subscriber) {if (AndroidDependenciesDetector.isAndroidSDKAvailable() &&     !AndroidDependenciesDetector.areAndroidComponentsAvailable()) {// Crash if the user (developer) has not imported the Android compatibility library.
        throw new RuntimeException("It looks like you are using EventBus on Android," +
                "make sure to add the \"eventbus\"Android library to your dependencies.");
    }

        // 1 从这里开始看,获取调用者的类对象。Class<?> subscriberClass = subscriber.getClass();
    //2 找到订阅类中的订阅办法
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    
    // 3 遍历订阅办法,将订阅者和其中的订阅办法绑定。synchronized (this) {for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);
        }
    }
}

Step1

看下步骤 2 中的 subscriberMethodFinder 的 findSubscriberMethods 办法

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {    
        // 1 首先从缓存 map 中拿到订阅类的订阅办法列表,应用了缓存进步性能,nice,不出所料 METHOD_CACHE 的类型是 Map<Class<?>, List<SubscriberMethod> >
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    //2 如果不为空,阐明之前该类已经注册过,该类的新对象不用从新做绑定了,因为此时的操作是类层面的
    if (subscriberMethods != null) {return subscriberMethods;}
        // 如果 subscriberMethods 为 null, 阐明该类是第一次注册,须要将其中的接管办法保存起来,//ignoreGeneratedIndex 默认为 false
    if (ignoreGeneratedIndex) {subscriberMethods = findUsingReflection(subscriberClass);
    } else {subscriberMethods = findUsingInfo(subscriberClass);
    }
    // 如果 subscriberMethods 为 null,阐明以后类对象没有生命订阅办法,抛出异样
    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;
    }
}

Step2

从步骤 2 中找出类的注册办法列表,而后遍历列表,调用上面的办法,将类对象和注册办法绑定。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //1 找到订阅办法的事件类型,即发送事件的 MessageEvent.class
    Class<?> eventType = subscriberMethod.eventType;
  
      //2 将订阅者类对象和订阅事件绑定成一个对象
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //subscriptionsByEventType 这个汇合必定是用来搁置同一事件类型的订阅汇合的,因为一个事件可能会有多个订阅的。CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
            // 如果一个订阅者屡次订阅了一个事件(@Subscribe 注解的办法的参数是同一类型),抛出异样
        if (subscriptions.contains(newSubscription)) {throw new EventBusException("Subscriber" + subscriber.getClass() + "already registered to event"
                    + eventType);
        }
    }

        //3 依照订阅办法中 @Subscribe 中的 priority 参数进行排序,默认为最低优先级 0。subscriptions 种的对象按优先级排序,收到事件后就会            按优先级进行回调
    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;
        }
    }

        // typesBySubscriber 类型 Map<Object, List<Class<?>>>,Key 为订阅者,value 为订阅者中的订阅办法,用来记录每个订阅者外部都订阅了哪些事件类型
    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);
        }
    }
}

2 post

/** Posts the given event to the event bus. */
public void post(Object event) {
        // 1 首先获取以后线程的工作状态
    PostingThreadState postingState = currentPostingThreadState.get();
    // 2 获取以后线程的工作队列
    List<Object> eventQueue = postingState.eventQueue;
    //3 将事件退出到事件队列
    eventQueue.add(event);
     //4 如果以后线程的工作状态没有正在发送事件 
    if (!postingState.isPosting) {
            // 标记 postingState 的是否是主线程,并将工作状态 isPosting 设为 true
        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;
        }
    }
}


/**
* 发送事件
* @param event 事件
* @param postingState 以后线程相干的配置
*/
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        // 如果应用继承事件的父类 / 接口,比方你发送了 MessageEvent 事件,如果该事件继承了 BaseEvent 和 Ievent 接口, 那么当你发送                        MessageEvent 事件时,零碎也会发送 BaseEvent 和 Ieven 事件
        if (eventInheritance) {
                // 遍历父类,将事件的父类 / 接口通通退出到 eventTypes 中
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {Class<?> clazz = eventTypes.get(h);
                // 遍历 eventTypes,顺次发送调用事件
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
           
        } else {
                // 不实用事件继承模型,间接发送该事件
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
                // 如果该事件没有订阅者抛出异样
            if (logNoSubscriberMessages) {logger.log(Level.FINE, "No subscribers registered for event" + eventClass);
            }
            //EventBus 外部也实用 EventBus 发送了一个异样事件
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {post(new NoSubscriberEvent(this, event));
            }
        }
    }
    

postSingleEventForEventType

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        // 1 获取该事件的所有订阅关系列表
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    //2 遍历订阅关系列表,顺次将事件发送到订阅者
    if (subscriptions != null && !subscriptions.isEmpty()) {for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            try {
                    // 将事件发送到订阅者
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {break;}
        }
        return true;
    }
    return false;
}

postToSubscription

这里就比拟要害了,最终到了事件散发的中央了。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //1 首先判断订阅关系中订阅办法的线程,就是申明线程时应用 @Subcribe 注解时传入的 threadMode 字段的值
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING: // 间接发送事件
            invokeSubscriber(subscription, event);
            break;
        case MAIN:  // 在主线程相应事件
                    // 事件收回线程是否是主线程
            if (isMainThread) { 是,间接发送
                invokeSubscriber(subscription, event);
            } else { 不是通过 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 转发
                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

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

在 postToSubscription 中有三个重要的角色 mainThreadPoster,backgroundPoster,asyncPoster

其中 mainThreadPoster 的类型是 HandlerPoster。其实就是 Handler。调用其 enqueu() 办法

而 backgroundPoster 和 asyncPoster 实质都是 Runnable

3 unregister

解绑办法就简略多了,重要的是就把 register 里提到的 2 个重要的几何中删除订阅者

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
    //1 从 typesBySubscriber 找到订阅者所订阅的事件类型列表
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        //2 遍历列表,顺次解绑订阅者和事件类型。应该是从 post 剖析里的订阅事件汇合 subscriptionsByEventType 里移除对应事件类型的该订阅者
        for (Class<?> eventType : subscribedTypes) {unsubscribeByEventType(subscriber, eventType);
        }
        // 3 移除订阅者
        typesBySubscriber.remove(subscriber);
    } else {logger.log(Level.WARNING, "Subscriber to unregister was not registered before:" + subscriber.getClass());
    }
}

/**
* 解绑订阅者和事件类型
* @param subscriber 订阅者
* @param eventType  订阅的事件类型
*/
 private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
                // 从 subscriptionsByEventType 里获取该订阅事件的订阅者汇合。List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                    // 遍历汇合,获取所有的订阅关系
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    // 重要。不然会抛出 ConcurrentModifyException
                    i--;
                    size--;
                }
            }
        }
    }

4 Subscribe 注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;}

5 ThreadMode

// 定义事件回调办法工作线程的类
public enum ThreadMode {
    /**
     * 间接在发送事件的线程里调用 Subscriber,这个是默认的设置,事件交付开销最小,因为它防止了线程切换。因而它是那种很快实现的单任务             * 默认的的线程工作模型。应用该模型的事件必须很快实现,因为当公布线程是主线程时,它可能阻塞主线程。/
    POSTING,

    /**
     * 在 Android 平台,订阅者将会在 Android 的主线程调用。如果公布线程时主线程,订阅办法将会被间接调用。进而阻赛公布线程,如果公布线             * 程不是主线程。事件将会排队期待散发。应用这种模式的订阅者必须疾速实现工作,防止阻赛主线程。非 Android 平台和 Posting 一样
     */
    MAIN,

    /**
     * 在 Android 平台,订阅者将会在 Android 的主线程调用。不同于 MAIN,事件将会有序散发。确保了 post 调用时非阻赛的。*/
    MAIN_ORDERED,

    /** 
     * 在 Android 平台,订阅者将会在后盾线程被调用,如果公布线程不是主线程,订阅者将会被间接调用,如果公布线程时主线程,那么 EventBus             * 应用后盾线程,进而有序散发所有事件,应用此模式的 Subscribers 应该疾速实现工作免得阻赛后台线程。非 Android 平台,总是用后盾线程                   * 相应事件 
     */
    BACKGROUND,

    /**
     * 订阅者将会在独自的线程被调用,总是独立于公布线程和主线程。公布事件从不会期待应用这种模式的订阅办法。如果订阅办法执行耗时工作,* 则应该应用此模式。比方;网络拜访。防止同时触发大量的长时间运行的异步订阅办法,从而限度并发的线程数量。EventBus 应用线程池来            * 高效的服用已实现异步订阅告诉的线程
     */
    ASYNC
}

6 EventBus2.0 和 EventBus3.0 的区别?

这是在面试过程中,面试官最常问的一个问题。

EventBus2.0 和 3.0 最大的区别有两点:

1.EventBus2.0 中咱们在书写订阅办法时的名字必须是 onEvent 结尾,而后通过命名不同来区别不同的线程模式。例如对应 posting 则命名为 onEvent(),onEventMainThread() 则对应 main 等。而 3.0 则能够用任何名字作为办法名称,只须要在办法名的后面用 @Subscribe 注解来进行正文,而后应用 threadMode 来设置在哪里线程中接管事件和处理事件

2.EventBus2.0 应用的是反射的形式来查找所有的订阅办法,而 3.0 则是在编译时通过注解处理器的形式来查找所有的订阅办法。性能上来说,3.0 比 2.0 要高的多。

退出移动版