事件经过起因背景,不表。

使用EventBus

根据官网https://github.com/greenrobot/EventBus的介绍。

1. 添加依赖

首先需要在gradle中添加

compile 'org.greenrobot:eventbus:3.0.0'

2. 三步走

1) 创建一个Event对象,没要求,创建一个class即可。

例如:

    public  class Event {
    }

2) 声明订阅函数,以及注册和反注册。

订阅:

@Subscribe
public void onEvent(Event e) {
    onReceive("onEvent", e);
}

注册/反注册。可以在Fragment的onResume和onDestroy/onPause中进行。

@Override
public void onResume() {
    super.onResume();
    EventBus.getDefault().register(this);
}

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

3) 发射数据

例如:

EventBus.getDefault().post(new Event());

3. 举个例子

下面是我写的Example:

public class EventBusFragment extends BNFragment {
    @BindView(R.id.result)
    TextView result;

    public EventBusFragment() {
        setLayoutId(R.layout.frag_event_bus);
    }

    @Override
    public void onResume() {
        super.onResume();
        EventBus.getDefault().register(this);
    }

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

    @OnClick(R.id.btn_subscribe)
    public void onClickSubscribe(View v) {
        EventBus.getDefault().post(new GhostEvent());
        EventBus.getDefault().post(new Event());
        EventBus.getDefault().post(new EventThreadMain());
        EventBus.getDefault().post(new EventThreadChild());
    }

    private void onReceive(String where, Object o) {
        final StringBuilder sb = new StringBuilder()
        .append(Looper.myLooper() == Looper.getMainLooper() ? "[MAIN]" : Thread.currentThread())
        .append(" ").append(where != null ? where + " : " : "")
        .append(o.getClass().getSimpleName()).append('\n');
        log(sb.toString());
    }
    ///

    public void onGhostEvent(GhostEvent e) {
        onReceive("onGhostEvent", e);
    }

    @Subscribe
    public void onEvent(Event e) {
        onReceive("onEvent", e);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMainEvent(EventThreadMain e) {
        onReceive("onMainEvent", e);
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onChildEvent(EventThreadChild e) {
        onReceive("onChildEvent", e);
    }

    public static class GhostEvent {
    }

    public static class Event {
    }

    public static class EventThreadMain {
    }

    public static class EventThreadChild {
    }
}

结果如下:

03-23 01:09:45.429 15030 15030 D EventBusFragment: (252610678) [MAIN] onEvent : Event
03-23 01:09:45.430 15030 15030 D EventBusFragment: (252610678) [MAIN] onMainEvent : EventThreadMain
03-23 01:09:45.433 15030 15129 D EventBusFragment: (252610678) Thread[pool-1-thread-1,5,main] onChildEvent : EventThreadChild

根据结果可以发现:

1)发射的GhostEvent,没有收到消息。为什么呢?因为我们使用的是默认配置,需要添加@Subscribe注解放会订阅成功。 2)EventThreadMain是在主线程中回调的而EventThreadChild是在子线程回调的。因为在注解中我指定了threadMode。 3)Event没有指定threadMode,但是在主线程回调了。因为默认使用的是POSTING,即回调会在发生在发射的线程中。案例中是在主线发射,因此会在主线程回调。

为什么默认情况下不添加@Subscribe注解便不会生效呢?如何找到对应的回调的呢?

EventBus内部工作原理

1. 注册流程

首先我们看看调用register之后EventBus干了些什么事情?废话不多说,我们看源码:

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

看到一开始会调用findSubscriberMethods, 其实这里是从订阅者的class中获取到所有的订阅函数。然后调用subscribe将订阅者和订阅函数(还有数据)进行绑定。

下面我看看findSubscriberMethods做了什么:

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

这段代码的意思很明确,如果METHOD_CACHE中有有关于某个订阅者类的缓存的话,直接使用缓存中的数据。如果没有的话,就会通过findUsingReflection或者findUsingInfo来生成订阅函数,然后加入缓存之后返回数据。

问题来了?findUsingReflectionfindUsingInfo有什么区别呢,因为我们使用的是默认的配置所以其实没多大区别,并且ignoreGeneratedIndex默认为FALSE(当然有区别啊!会涉及到一些高级功能,比如增加了拓展性,有兴趣可以继续看源码。其实实现很简单)。如果你去查看(断点)findUsingInfo,你会发现其实它和findUsingReflection是一模一样的。最终都调用了findUsingReflectionInSingleClass

这个函数的作用字面上已经很清楚了:利用反射获取单个类的订阅函数。单个其实只是相对于class自己而言,并不会去找它的parent。这里说的不找parent只是说本函数。其实findUsingReflection或者findUsingInfo都会不断通过findState.moveToSuperclass()去那parent,然后继续调用这个反射。moveToSuperclass中遇到”java.”/”javax.”/”android.”开通的class都会过滤掉,想想如果你的类集成关系比较复杂,嵌套很深的话,呵呵(这里不讨论如何去优化)。

下面继续研究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();
        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");
        }
    }
}

首先,它会通过getDeclaredMethods获取method列表,如果异常才会用getMethods。如果使用后者的话,就会忽略skipSuperClasses。

继续往下面看,可以发现函数的modifier如果不是Public或者包含MODIFIERS_IGNORE的话,就不会被订阅到的。(其中MODIFIERS_IGNORE包含了ABSTRACT、STATIC等)。

接着往下走,我们会看到Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);这一行,如果不为空的话才会继续,如果否则抛弃。这就解释了为什么上面的GhostEvent没有被订阅。

所以,如果我们发现自己写的订阅函数没生效的话,首先看看是不是public,有没有@Subscribe注解,是不是static等等。并且要主要,订阅函数只能有一个参数,切记切记。

下面回到subscribe,看看它发生了什么:

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } 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);
        }
    }
}

其实subscribe中主要是围绕EventType和Subscriber为key建立索引,方便日后找到对应关系。

2. 发射流程

当我们调用了post到我们的订阅函数收到回调,这个过程是怎样的呢,经历了什么?目前还不得而知。所以回到源码,从post入手看看究竟发生了什么:

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

可以看到,首先会从currentPostingThreadState(ThreadLocal), 拿到当前线程中的PostingThreadState。然后将我们的事件加入到它的发送队列里面。加入队列后,检查当前队列是不是posting状态,如果不是的话,就会在while循环中调用postSingleEvent(eventQueue.remove(0), postingState),一个一个发送消息出去。

那么一个消息是怎样发送出去的呢?这个过程还需要继续看postSingleEvent是如何实现的。

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);
    }
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

这部分逻辑区别在于eventInheritance这里,默认是为true的。看lookupAllEventTypes的实现可以发现,EventType的super class和interfaces也会被加入进来,这部分逻辑略过。主要看postSingleEventForEventType的实现过程。

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) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            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;
}

在这里我们看到了subscribe()的时候提到的subscriptionsByEventType,即通过EventType获取到所有的订阅者。然后遍历所有的订阅者一个一个通过postToSubscription发射到订阅者实例。(这里说订阅者其实不对,应该说的是订阅函数或者订阅关系。因为对于同一个类型的Event,同一个订阅者可以实现多个订阅函数。并且,subscriptionsByEventType的数据结构本身就是Subscription。就是它包含了订阅者和订阅函数。)如下:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(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);
    }
}

上面代码就可以用到我们前面说的threadMode了。是不是一下子就知道这四种状态到底TM干了什么哈。其实他们的区别无非就在于线程切换,不表。我们直接看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);
    }
}

其实就是拿到订阅函数里面的函数然后反射调用回订阅者里面去。

3. 解绑订阅

其实主要是把上面提到的subscribe函数里面的行为倒过来,一个一个从内存中删除掉。不表了。

结语

总是要说点什么吧。其实这里只是说了如何使用。以及注册、发送的时候到底做了什么。其实还有一些高级的使用方法,如果你发现不能满足自己的需求,那么多看看源码。

By @hyongbai 共14786个字

本文链接 http://yourbay.me/all-about-tech/2017/03/23/event-bus/