EventBus 3.0 使用及源码解析

1.为什么会诞生EventBus

在Android开发过程中,我们经常有这样的需求:位于栈顶的Activity需要关闭非栈顶的Activity或调用其中的方法(比如刷新数据,更新UI等)

例:现在有3个Activity,分别是:主界面(A),个人中心(B),头像裁剪&上传页面(C),在这三个页面中都有一个显示用户头像的ImageView。当用户进入C页面选择图片裁剪并上传至后台服务器后。我们要保证ABC三个页面的头像显示一致,那么我们就来看看不用EventBus怎么实现这样的功能。

普遍的做法大概就这三种,如果还有其他方法,欢迎在评论区留言。

  • 在AB两个Activity中暴露一个public static的方法,供C修改成功后调用。
  • 在AB两个Activity中注册广播,当C修改成功后,发送对应的广播,完成修改。
  • 通过startActivityForResult和onActivityResult来实现。

下面我们分析一下这三种方法的实现:

第一种:由于是静态方法,所以使用这种方法时,要将ImageView设置为全局静态变量,浪费宝贵的内存资源。

第二种:发送一个广播做通知,其实是一个不错的选择,发送广播然后实现广播类做回调通知。但是如果现在需求是对一个Fragment做通知。这个时候广播就派不上用场了。毕竟广播只能再Activity中注册发送广播,如果硬要这么做,又涉及到Fragment与Activity之间的数据交互问题。

第三种:代码量巨大并且逻辑有点绕,开发和维护成本都很大。

这三种方法,没有完全的对与错,好与坏,仁者见仁智者见智。

想想这些方法,真的难用啊,就没有方便快捷的方法吗?

EventBus 3.0 使用及源码解析_第1张图片
没时间解释了,快上车吧

滴滴滴,就在这个时候,一辆EventBus缓缓驶来,你发现驾驶员居然是一位神仙(old driver)。一种全局的 观察者模式,你可以把你的愿望(Event)告诉(Post)这位神仙(old driver),他有很多小老弟(多线程),就算竭尽全力(不费吹灰之力)也要让你的愿望在你指定的地方实现。

EventBus 3.0 使用及源码解析_第2张图片
EventBus-Publish-Subscribe.png

2.下面我们看看EventBus怎么使用。

GitHub地址:

https://github.com/greenrobot/EventBus

Android Studio Gradle配置:

compile 'org.greenrobot:eventbus:3.1.1'

Eclipse

卸载Eclipse,并安装Android Studio

最基础的使用方法

public class EventBusActivity extends AppCompatActivity {

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void canBeAnyNameHereHaHaHa(MyTestEvent event) {
        // 收到消息后打印
        Toast.makeText(this, event.getMsg(), Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus);
        // 第一步:注册订阅者身份
        EventBus.getDefault().register(this);
        // 第二步:发送消息给自己(双重身份:订阅者&发布者)
        EventBus.getDefault().post(new MyTestEvent("Hello EventBus!"));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销之前先判断是否已注册
        if (EventBus.getDefault().isRegistered(this))
            // 关闭页面的时候,一定要注销,不然会造成内存泄漏
            EventBus.getDefault().unregister(this);
    }

}

我们结合上面提到的修改头像为例,来看看用EventBus应该怎么做

  1. 定义通知实体类(也可不定义,使用String,Integer等任意类型都可以)
/**
 * Created by ZengCS on 2018/8/17.
 * E-mail:[email protected]
 */
public class UpdateAvatarEvent {
    private String url;// 头像云端地址

    public UpdateAvatarEvent(String url) {
        this.url = url;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}
  1. 在需要订阅通知的地方进行注册和注销,并写一个订阅方法(方法需要写注解 @Subscribe(threadMode = ThreadMode.XXX))
    每个订阅者可以订阅多种通知类型,没有限制。

官方推荐注册&注销方式:

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

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

但是官方推荐的方式经常出现灵异事件,所以大牛们还是推荐这样写:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    // 注册EventBus
    EventBus.getDefault().register(this);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 注销之前先判断是否已注册
    if (EventBus.getDefault().isRegistered(this))
        EventBus.getDefault().unregister(this);
}

下面我们看看在主页和个人中心如何订阅修改头像的通知:

/**
 * Created by ZengCS on 2018/8/17.
 * E-mail:[email protected]
 */
public class IndexActivity extends AppCompatActivity {
    private ImageView mImageView;

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onUpdateAvatarEvent(UpdateAvatarEvent event) {
        if (event == null) return;
        String url = event.getUrl();
        if (!TextUtils.isEmpty(url))
            Glide.with(this)
                    .load(url)
                    .into(mImageView);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_index);
        mImageView = findViewById(R.id.id_iv_avatar);
        // 注册EventBus
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销之前先判断是否已注册
        if (EventBus.getDefault().isRegistered(this))
            EventBus.getDefault().unregister(this);
    }
}
/**
 * Created by ZengCS on 2018/8/17.
 * E-mail:[email protected]
 */
public class UserCenterActivity extends AppCompatActivity {
    private ImageView mImageView;

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onUpdateAvatarEvent(UpdateAvatarEvent event) {
        if (event == null) return;
        String url = event.getUrl();
        if (!TextUtils.isEmpty(url))
            Glide.with(this)
                    .load(url)
                    .into(mImageView);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_center);
        mImageView = findViewById(R.id.id_iv_avatar);
        // 注册EventBus
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销之前先判断是否已注册
        if (EventBus.getDefault().isRegistered(this))
            EventBus.getDefault().unregister(this);
    }
}
  1. 在任意地方调用EventBus.getDefault().post(object)
/**
 * Created by ZengCS on 2018/8/17.
 * E-mail:[email protected]
 */
public class EditAvatarActivity extends AppCompatActivity {
    // 非相关代码已删除,这里假装头像上传成功了,并把云端头像地址返回给我们了
    public void onUploadSuccess(String avatarUrl) {
        // 发送更新头像通知
        EventBus.getDefault().post(new UpdateAvatarEvent(avatarUrl));
    }
}
如此方便快捷的使用方式,确实是做APP层消息通知的不二选择。不过在注册过EventBus的类中,千万别忘了在结束的时候注销。

这么好用的东西还是需要去了解一下其内部实现的。而且EventBus内部实现其实并不复杂。

3.EventBus源码解析

获取EventBus实例对象

/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

可以看到,这是一个典型的DCL单例模式。单例模式获取一个EventBus对象,保证所有的EventBus对象是同一个。

下面我们来看看构造函数:

/**
 * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
 * central bus, consider {@link #getDefault()}.
 */
public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}

两个构造方法,一个是无参的public的方法,另外一个是内部方法,带参数的。

内部构造方法中创建了几个容器,用来装各种订阅者信息的,很关键的几个成员我们需要注意,mainThreadPoster 、backgroundPoster 、asyncPoster ,这个几个是后来post消息的时候,用的到的,就是利用他们将消息在不同的线程发送出去,达到在不同线程中执行的效果。

这边DEFAULT_BUILDER声明如下:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

我们看看EventBusBuilder在做什么:

/*
 * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.greenrobot.eventbus;

import android.os.Looper;

import org.greenrobot.eventbus.meta.SubscriberInfoIndex;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Creates EventBus instances with custom parameters and also allows to install a custom default EventBus instance.
 * Create a new builder using {@link EventBus#builder()}.
 */
public class EventBusBuilder {
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

    boolean logSubscriberExceptions = true;
    boolean logNoSubscriberMessages = true;
    boolean sendSubscriberExceptionEvent = true;
    boolean sendNoSubscriberEvent = true;
    boolean throwSubscriberException;
    boolean eventInheritance = true;
    boolean ignoreGeneratedIndex;
    boolean strictMethodVerification;
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
    List> skipMethodVerificationForClasses;
    List subscriberInfoIndexes;
    Logger logger;
    MainThreadSupport mainThreadSupport;

    EventBusBuilder() {
    }

    /** Default: true */
    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
        this.logSubscriberExceptions = logSubscriberExceptions;
        return this;
    }

    /** Default: true */
    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
        this.logNoSubscriberMessages = logNoSubscriberMessages;
        return this;
    }

    /** Default: true */
    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
        return this;
    }

    /** Default: true */
    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
        this.sendNoSubscriberEvent = sendNoSubscriberEvent;
        return this;
    }

    /**
     * Fails if an subscriber throws an exception (default: false).
     * 

* Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, you won't miss * exceptions during development. */ public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) { this.throwSubscriberException = throwSubscriberException; return this; } /** * By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified). * Switching this feature off will improve posting of events. For simple event classes extending Object directly, * we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be * >20%. *

* However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app, * unless it is posting at high rates, e.g. hundreds/thousands of events per second. */ public EventBusBuilder eventInheritance(boolean eventInheritance) { this.eventInheritance = eventInheritance; return this; } /** * Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced * setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior. */ public EventBusBuilder executorService(ExecutorService executorService) { this.executorService = executorService; return this; } /** * Method name verification is done for methods starting with onEvent to avoid typos; using this method you can * exclude subscriber classes from this check. Also disables checks for method modifiers (public, not static nor * abstract). */ public EventBusBuilder skipMethodVerificationFor(Class clazz) { if (skipMethodVerificationForClasses == null) { skipMethodVerificationForClasses = new ArrayList<>(); } skipMethodVerificationForClasses.add(clazz); return this; } /** Forces the use of reflection even if there's a generated index (default: false). */ public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) { this.ignoreGeneratedIndex = ignoreGeneratedIndex; return this; } /** Enables strict method verification (default: false). */ public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) { this.strictMethodVerification = strictMethodVerification; return this; } /** Adds an index generated by EventBus' annotation preprocessor. */ public EventBusBuilder addIndex(SubscriberInfoIndex index) { if (subscriberInfoIndexes == null) { subscriberInfoIndexes = new ArrayList<>(); } subscriberInfoIndexes.add(index); return this; } /** * Set a specific log handler for all EventBus logging. *

* By default all logging is via {@link android.util.Log} but if you want to use EventBus * outside the Android environment then you will need to provide another log target. */ public EventBusBuilder logger(Logger logger) { this.logger = logger; return this; } Logger getLogger() { if (logger != null) { return logger; } else { // also check main looper to see if we have "good" Android classes (not Stubs etc.) return Logger.AndroidLogger.isAndroidLogAvailable() && getAndroidMainLooperOrNull() != null ? new Logger.AndroidLogger("EventBus") : new Logger.SystemOutLogger(); } } MainThreadSupport getMainThreadSupport() { if (mainThreadSupport != null) { return mainThreadSupport; } else if (Logger.AndroidLogger.isAndroidLogAvailable()) { Object looperOrNull = getAndroidMainLooperOrNull(); return looperOrNull == null ? null : new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull); } else { return null; } } Object getAndroidMainLooperOrNull() { try { return Looper.getMainLooper(); } catch (RuntimeException e) { // Not really a functional Android (e.g. "Stub!" maven dependencies) return null; } } /** * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be * done only once before the first usage of the default EventBus. * * @throws EventBusException if there's already a default EventBus instance in place */ public EventBus installDefaultEventBus() { synchronized (EventBus.class) { if (EventBus.defaultInstance != null) { throw new EventBusException("Default instance already exists." + " It may be only set once before it's used the first time to ensure consistent behavior."); } EventBus.defaultInstance = build(); return EventBus.defaultInstance; } } /** Builds an EventBus based on the current configuration. */ public EventBus build() { return new EventBus(this); } }

里面是一些EventBus的基础配置,打印异常信息,发送未被订阅的消息等。
特别需要注意的是 ignoreGeneratedIndex 这个变量是用来确定EventBus用什么方式来获取订阅方法。
true代表的是不优先使用索引,而用反射的方式,false代表的是优先使用索引。默认是false。
这边有个addIndex方法是用来添加订阅方法索引的,这是3.0版本的新特性,也是高级用法。
在EventBus带参数的构造函数里面初始化了 SubscriberMethodFinder 这个类,三个参数分别为索引列表,
是否严格方法定义,是否无视索引。

SubscriberMethodFinder(List subscriberInfoIndexes, boolean strictMethodVerification,
                       boolean ignoreGeneratedIndex) {
    this.subscriberInfoIndexes = subscriberInfoIndexes;
    this.strictMethodVerification = strictMethodVerification;
    this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}

getDefault总结:获取一个EventBus的单例对象,用于任何EventBus相关的操作,比如:注册,注销,发送等。

注册

/**
 * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
 * are no longer interested in receiving events.
 * 

* Subscribers have event handling methods that must be annotated by {@link Subscribe}. * The {@link Subscribe} annotation also allows configuration like {@link * ThreadMode} and priority. */ public void register(Object subscriber) { Class subscriberClass = subscriber.getClass(); List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }

首先是通过一个findSubscriberMethods方法找到了一个订阅者中的所有订阅方法,返回一个 List,然后遍历所有的订阅方法,做订阅操作。

进入到findSubscriberMethods看看如何实现的。

List findSubscriberMethods(Class subscriberClass) {
    List 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是怎么实现的
private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
这里使用了ConcurrentHashMap来对所有的方法进行缓存,为什么要使用ConcurrentHashMap来做方法的缓存呢?无疑是看中了ConcurrentHashMap线程安全且效率高的特性,更多关于ConcurrentHashMap的特性,请自行查阅相关资料。

回到findSubscriberMethods中,首先是通过类来当key,查找在当前缓存中是否存在这个类的订阅方法列表。有就直接return 缓存中的列表。

没有就进入下一步,这边有两种方式来获取类中的订阅方法通过ignoreGeneratedIndex来确定获取订阅方法的方式为true时会使用反射方法获取订阅者的事件处理函数,为false时会优先使用subscriber Index(订阅方法索引)生成的SubscriberInfo来获取订阅者的事件处理函数(减少反射耗时),默认false。

使用订阅方法索引需自己构建一个EventBus对象,本文暂时不讲解索引方式。

下面我们进入findUsingReflection看看是如何用反射获取到订阅方法的

private List 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. 获取订阅类声明的所有方法; 然后对获取到的方法全部遍历一遍
  2. 获取方法的修饰符:即方法前面的public、private等关键字。
  3. 如果该类方法使用了@Subscribe标注、方法中只有一个参数、且方法修饰符为public。findState.checkAdd(method, eventType) 如果之前没有存在过则返回true
  4. 判断@Subscribe标注中的threadMode对应的值,默认模式ThreadMode.POSTING
  5. 创建一个SubscriberMethod(普通的JavaBean)对象,该对象很简单就是保存有方法、方法参数类型、线程模式、订阅的优先级、sticky标志位。并将该对象添加到FindSate的List集合中。

订阅

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList 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> 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).
            Set, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry, 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);
        }
    }
}

解读:

  1. 获取方法参数类型;注意:使用@Subscribe标注的方法有且仅有一个参数
  2. 利用订阅者对象及其事件处理方法构建一个Subscription对象,该对象存储有Object、SubscriberMethod对象
  3. 从subscriptionsByEventType集合中获取当前事件对应的Subscription对象集合; 如果得到的集合为空则创建一个这样的集合,并将刚创建的Subscription对象添加进subscriptionsByEventType集合中;如果得到的集合不为空且刚创建的Subscription对象已经存在该集合中则抛出异常,即同一个对象不能注册两次!
  4. 将第二步创建的Subscription对象按照优先级存入Subscription对象集合中,该集合中的元素都是按照优先级从高到低存放.
  5. 以subscriber对象为键,从typesBySubscriber获取该对象对应的接收事件类型集合,没有则创建一个这样的集合,然后当前事件类型添加到该集合中,最后将整个集合添加进typesBySubscriber集合中;有则直接添加到接收事件类型集合中;
  6. sticky 该值默认为false,除非在注册事件方法时使用了如下的标注@Subscribe(sticky = true);那么就会执行到这里。stickyEvent也是EventBus3.0的一大特点,该类事件一旦发送给EventBus,那么EventBus就会将它存入Map

发布订阅事件

/** Posts the given event to the event bus. */
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        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;
        }
    }
}
 
 
  1. 获取当前线程对应的一个PostingThreadState()对象
  2. 向PostingThreadState的事件队列中添加一个事件
  3. 从PostingThreadState的事件队列——eventQueue中移出一个事件,并调用postSingleEvent方法进行派送

下车

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
    List> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
  1. 从typesBySubscriber获取该对象接收的事件类型集合;
  2. 对得到的接收事件类型集合中的每个事件类型调用unsubscribeByEventType进行处理;跟着我们就分析该方法
  3. 该对象从typesBySubscriber集合中移除;
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class eventType) {
    List 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);
                i--;
                size--;
            }
        }
    }
}
  1. 从subscriptionsByEventType集合中获取该事件类型对应的Subscription集合
  2. 如果集合中的元素——Subscription的subscriber域等于目标subscriber,则将该Subscription从subscriptionsByEventType集合中移除出去;

没有精确去分析所有的源码。这边大概做一下总结,整个流程大概如下。

  1. 注册EventBus,EventBus会获取当前类中的订阅方法,包括方法的参数,类型,优先级等信息。在获取的方法中,会先遍历缓存。如果在缓存中没有才去调用获取方法。
  2. 获取订阅方法有两种方式,一种是反射,反射获取全部方法,找到加了@Subscribe注解并有一个参数的方法,另外一种是通过订阅方法索引来得到订阅方法。反射的效率较低,所以优先使用用订阅方法索引来获取,当索引列表找不到这个方法。它就会走反射的途径。注册过后这个订阅方法就被存起来了。等待接受消息。
  3. 在3.0以前是没有注解方法的那时候都是通过反射来获取和执行方法。而且方法必须以onEvent开头,分别为onEventMainThead,onEventBackground,onEvent,onEventAsync,3.0以后就全部替换为注解的形式,方法名可以是任意(只要能通过编译的都可以)
  4. postEvent,会把这个Event加入消息队列,然后通过Event的类型信息来得到它的订阅方法,然后根据相应的订阅方式反射调用订阅方法。没有选择的threadMode的post一般都会在UI线程执行。如果当前post不是UI线程,这边会用Handle的机制来让接受Event的方法运行在UI线程。
  5. 解除注册,将该对象从对象缓存列表中移除,获取当前对象的订阅列表,然后将其从订阅列表移除

你可能感兴趣的:(EventBus 3.0 使用及源码解析)