EventBus简单剖析

前言

最近在重温EventBus的源码,于是想写篇博文,记录一下。既然说到了EventBus,那就先从EventBus的使用开始吧。

 

一、EventBus的使用:

(1)在项目的build.gradle下,引入EventBus

//EventBus
implementation "org.greenrobot:eventbus:3.1.1"

(2)在Activity或Fragment的onCreate方法中进行register:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    EventBus.getDefault().register(this);
}

(3)在Activity或Fragment的onDestory方法中进行unregister:

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

(4)创建事件实体类

public class MessageEvent {

    private String message;

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

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

(5)在Activity中register事件:

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent messageEvent){
     Log.e("=========","=========="+messageEvent.getMessage());
}

(6)发送Event事件:

EventBus.getDefault().post(new MessageEvent("信息来了"));

二、代码剖析

(1)EventBus在register的时候,调用的是

EventBus.getDefault.register(this);

我们先来看EventBus的getDefault方法:

static volatile EventBus defaultInstance;

public EventBus() {
     this(DEFAULT_BUILDER);
}

public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
}

 由代码可知,这里用到了单例模式中DCL双重校验的单例模式,但是我们都知道单例模式的构造方法都是私有的,但是这里为什么使用了public关键字修饰呢?

这是因为EventBus这样设计,可以同时开始多条事件总线而不会彼此影响。因此开发的时候也要注意,不能一会通过builder构造,一会用getDefault()获取,如果不弄清楚可能造成订阅者无法处理发布消息的情况。

接着往下看:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

public static EventBusBuilder builder() {
     return new EventBusBuilder();
}


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;

}

这里用到了设计模式中的构建者模式 ,而EventBus的事件总线用到了设计模式中的观察者模式。目前已知的EventBus用到了三种设计模式:单例模式、构建者模式、观察者模式。(如大家还知道用到的别的设计模式,可以帮我留言补充哦)

研究完了getDefault方法,我们再看register方法:

 public void register(Object subscriber) {
       Class<?> subscriberClass = subscriber.getClass();//获取当前的Class,如Activity或者Fragment对应的class

       //调用SubscriberMethodFinder类中的findSubscriberMethods方法,通过subscriberClass,找到subscribeClass中的所有订阅方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

       //找到订阅类中的所有订阅方法后,把这些订阅方法进行遍历
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
}

这里我们重点关注一下 findSubscriberMethods方法:

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

        //如果缓存中找到的订阅方法为空,那么就执行下面的代码,注意ignoreGeneratedIndex,如果没有设置,那么ignoreGeneratedIndex默认为false

        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是个ConcurrentHashMap:

private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();

而这个ConcurrentMap是以什么为键?什么为值?

分析代码可知,这个ConcurrentMap,是以订阅的类(如Activity或Fragment对应的Class)为键,以订阅的类中所有订阅的方法的集合为Value,看Value的类型List<SubscriberMethod>就可以看出。而这里的SubscriberMethod是对订阅方法信息的封装:

public class SubscriberMethod {
    final Method method;//订阅方法的Method对象
    final ThreadMode threadMode;//订阅方法的线程模式
    final Class<?> eventType;//订阅方法的事件类型(即订阅方法的参数类型)
    final int priority;//订阅方法的优先级,优先级越大的优先收到消息,默认优先级为0
    final boolean sticky;//订阅方法的事件是否为粘性事件
    /** Used for efficient comparison */
    String methodString;

    ...
}

而这里涉及到的ThreadMode,我们简单说一下ThreadMode:

public enum ThreadMode {

    //订阅者方法将在发布事件所在的线程中被调用。这是默认的线程模式。
    POSTING,


    //订阅者方法将在主线程(UI线程)中被调用。因此,可以在该模式的订阅者方法中直接更新UI界面。如果发布事件的线程是主线程,那么该模式的订阅者方法将被直接调用。使用该模式的订阅者方法必须快速返回,以避免阻塞主线程。
    MAIN,


    //订阅者方法将在主线程(UI线程)中被调用。 与MAIN不同,该事件将始终排队等待发送。 这可确保后置调用是非阻塞的。
    MAIN_ORDERED,


    //这种模式无论发布者是在主线程或者是那一条子线程中发布消息,接收函数的肯定是在子线程中,并且是这样理解:如果是在主线程中发布消息,那么就会随机开辟一条子线程来接收消息。如果是在子线程中发布消息,那么就会在相同的子线程来接收消息。
    BACKGROUND,


    //这种模式是无论你在那个线程中发布消息都会在不同的线程中接受消息。如果你在主线程中发布消息,就会随机的开辟一条子线程来接收消息;如果是在子线程中发布消息,就会开辟一条不同的子线程来接收消息。
    ASYNC
}

 好了,这个ThreadMode的小插曲结束,我们接着上面看findUsingInfo这个方法:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
}

看里面的findUsingReflectionInSingleClass(findState)方法:

//这个方法是通过反射获取所有的订阅方法
private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        //通过反射获取订阅类中的所有方法(包括带@Subscribe注解的和不带@Subscribe注解的)
        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();
            //如果修饰符是public的,并且不带static和abstract修饰符的
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                //获取方法的参数类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                //如果只有一个参数
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    //如果方法被@Subscribe修饰(获取到的subscribeAnnotation不为null,则证明方法被@Subscribe)
                    if (subscribeAnnotation != null) {
                        //执行到这里的方法,就确定为标准的EventBus的订阅方法了
                        //获取订阅方法的参数类型
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                           //获取订阅方法的ThreadMode
                            ThreadMode threadMode = subscribeAnnotation.threadMode();

                            //然后把订阅方法的信息封装到SubscriberMethod中,然后添加到findState的subscriberMethods中
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {//如果带有@Subscribe注解的方法参数数量不是1,那么会抛出以下异常
                    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)) {//如果方法被@Subscribe注解并且被public或者static、abstract修饰的就会抛出如下的异常
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
}

注意:这里的subscriberMethods为FindState类中的一个ArrayList集合:

static class FindState {
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        ...
}

到这里通过反射找到订阅类中的所有订阅方法结束了。下面我们来看一下找到所有的订阅方法后,所做的操作,我们返回到

public void register(Object subscriber)
synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
}

主要看subscribe方法:

 // Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //通过封装订阅方法类,获取到订阅方法对应的事件类型
        Class<?> eventType = subscriberMethod.eventType;
        //把每一个订阅方法和他对应的订阅者封装成一个Subscription类的对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

        //通过事件类型,获取订阅了该事件的所有订阅者subscriber的集合
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

        //如果subscriptions为空说明,还没有订阅者订阅该事件
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //如果subscriptions不为空说明,并且当前的订阅者也订阅了该事件,则抛出以下的异常
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

    ...
}

好了,通过以上的剖析,我们大概知道了EventBus在register 的时候,大概做了哪些事。下面我们来剖析下,看看EventBus在post发送事件的时候,做了哪些事。

(2)EventBus在post的时候,调用的是:

EventBus.getDefault().post(new MessageEvent("信息来了"));

看这里的post方法的源码:

public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> 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;
            }
        }
}

这里我们看到currentPostingThreadState是个ThreadLocal,通过currentPostingThreadState的get()方法,获取到了PostingThreadState对象,里面封装了当前线程的一些发送的事件的信息:

final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<>();//事件队列
        boolean isPosting;
        boolean isMainThread;//当前发送的线程是否是主线程
        Subscription subscription;
        Object event;
        boolean canceled;
}

再看post方法中调用的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) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
}

再看postSingleEvent方法中调用的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;
}

再看postSingleEventForEventType方法调用的postToSubscription方法:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING://默认的ThreadMode,直接执行订阅方法
                invokeSubscriber(subscription, event);
                break;
            case MAIN://如果发送线程是在主线程,那么直接执行订阅方法。如果发送消息的线程不是在主线程,那么需要通过MainThreadPoster切换到主线程中执行订阅方法
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED://如果mainThreadPoster 不为空,通过mainThreadPoster加入到事件队列中执行订阅方法,否则直接执行订阅方法,不会出现堵塞
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND://如果发送事件的线程是在主线程,那么需要通过backgroundPoster切换到子线程中执行订阅方法;如果发送事件的线程是在子线程,那么直接执行订阅方法。这里的BackgroundPoster继承了Runnable
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC://不管发送事件的线程是在主线程还是子线程,都会通过asyncPoster开启一个新的子线程执行订阅方法,这里的AsyncPoster同样继承了Runnable
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
}

发送的关键就在这个 postToSubscription方法,这个方法根据订阅方法的ThreadMode来执行不同的操作。

(3)EventBus在post的时候,调用的是:

EventBus.getDefault().unregister(this);

unregister方法:

/** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List<Class<?>> 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());
        }
}

unregister方法主要做的是从之前订阅的集合中移除订阅事件的操作。

总结

EventBus我们做了一个简单的剖析,了解到EventBus中使用到的一些设计模式,以及重点了解到了EventBus在注册的过程中所作的一系列的操作。

用简洁的语言描述一下EventBus的实现原理就是:

EventBus在使用的时候会首先书写EventBus.getDefault().register(this);EventBus在register(注册)的时候,会通过反射找到订阅者里面所有添加了@Subscribe注解的订阅方法,以当前订阅者为键,当前订阅者中所有订阅方法组成的集合为value,存储到一个ConcurrentHashMap中。然后遍历订阅方法的集合,再把订阅方法以及订阅方法对应的订阅者封装成一个Subscription对象。再以事件类型(即订阅方法的参数类型)为键,把该事件类型对应的所有Subscription对象添加到一个CopyOnWriteArrayList集合中,然后以这个集合作为HashMap的值存起来。

然后在发布者发布事件(即执行EventBus.getDefault().post(...))的时候,根据发布的事件类型来找到该事件类型所对应的这个Subscription对象的集合,遍历这个集合,根据订阅方法ThreadMode,以反射的方式调用订阅者当中的方法。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值