EventBus3.0源码分析(一)事件注册与发布流程

2018-07-18  本文已影响0人  kakaxicm

引言

EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,可以实现线程与Android组件之间的通信。EventBus使用简单,并将事件发布和订阅充分解耦,从而使代码更简洁。目前网上已经有很多优秀的分析它源码的文章,这里主要是为了自己深入理解学习,做备忘用。

简单使用

1.Gradle配置:

implementation 'org.greenrobot:eventbus:3.1.1'

2.构造消息实体类:

package com.qicode.kakaxicm.enventbusdr;

/**
 * Created by chenming on 2018/7/17
 */
public class MsgEvent {

    String value;

    public MsgEvent(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

3.注册和注销订阅者

public class MainActivity extends AppCompatActivity {

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

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);//注册订阅者
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);//注销订阅者
    }
}

3.定义事件接受者方法:

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true, priority = 100)
 public void onMessageEvent(MsgEvent event) {
       Toast.makeText(this, event.getValue(),Toast.LENGTH_LONG).show();
 }

这里的Subscribe注解描述了这个方法的运行线程,是否是粘性事件和优先级,关于这个注解,后面会分析。onMessageEvent方法运行在主线程,非粘性,优先级100.
注意事件接收方法为public,针对同一事件可以定义多个接收方法,如下:

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 100)
public void onMessageEvent(MsgEvent event) {
   Toast.makeText(this, event.getValue(), Toast.LENGTH_LONG).show();
}

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 101)
public void onMessageEvent1(MsgEvent event) {
    Toast.makeText(this, event.getValue()+"11111", Toast.LENGTH_LONG).show();
}

onMessageEvent 和onMessageEvent1都可以接收到MsgEvent,后者方法优先级跟高,会先执行。
4.发送事件:

    /**
     * 发送事件
     * @param view
     */
    public void onSend(View view) {
        //构造事件消息,发送
        MsgEvent event = new MsgEvent("haha");
        EventBus.getDefault().post(event);
    }

可以发现事件接收方法可以通过注解灵活配置,与传统的Handler发送消息相比,代码简洁,维护方便。当然了,EventBus的内部订阅事件和接收事件的线程切换也是通过Handler处理的。

EventBus源码分析

创建EventBus

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

下面我们看看主要属性:

    static volatile EventBus defaultInstance;
    //默认构造器
    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
    //key为事件类型,value为订阅了该类型事件的订阅者集合。看名字是做缓存订阅者用
    private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
    //key为事件类型Class,value为订阅者集合,一个事件可以有多个订阅者
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    //key为订阅者对象,value为这个订阅者订阅的事件Class集合
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    //粘性事件 key:粘性事件的class对象, value:事件对象
    private final Map<Class<?>, Object> stickyEvents;
    //用于android构造主线程Poster的接口
    private final MainThreadSupport mainThreadSupport;
    //事件转发到主线程处理
    private final Poster mainThreadPoster;
    //事件转发到后台线程处理
    private final BackgroundPoster backgroundPoster;
    //事件转发到异步线程处理
    private final AsyncPoster asyncPoster;
    //用于查找订阅方法
    private final SubscriberMethodFinder subscriberMethodFinder;
    //线程切换到非UI线程执行订阅的方法体
    private final ExecutorService executorService;
    //是否支持事件继承
    private final boolean eventInheritance;
    //各种开关
    private final boolean throwSubscriberException;
    private final boolean logSubscriberExceptions;
    private final boolean logNoSubscriberMessages;
    private final boolean sendSubscriberExceptionEvent;
    private final boolean sendNoSubscriberEvent;

可以看出核心的属性主要包括描述事件与订阅者的多对多映射集合,三中用于线程切换的转发器以及查询订阅方法的SubscriberMethodFinder。
再看建造方法方法:

public EventBus() {
        this(DEFAULT_BUILDER);
}
//通过Builder模式构造
EventBus(EventBusBuilder builder) {
        //日志
        logger = builder.getLogger();
        //事件类型与订阅者的多对多映射集合
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        //粘性事件集合
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        //三个Poster
        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;
        //任务切换到非UI线程的任务执行线程池
        executorService = builder.executorService;
}

注册源码分析

注册流程

    /**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * 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) {
        //1.订阅方法的"宿主"订阅对象Class
        Class<?> subscriberClass = subscriber.getClass();
       //2.查找订阅对象上的订阅方法集合
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
           //3.遍历订阅方法集合,根据SubscriberMethod和订阅者对象配置映射集合
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

执行流程已经在注释中写明,我们发现这个方法主要围绕SubscriberMethod来执行构造和订阅流程的,所以先看看它到底是啥:

public class SubscriberMethod {
    final Method method;//订阅的方法Method
    final ThreadMode threadMode;//执行在哪种线程
    final Class<?> eventType;//订阅方法的入参事件类型
    final int priority;//执行优先级
    final boolean sticky;//是否是粘性事件
    /** Used for efficient comparison */
    String methodString;//类似于Method的签名,所属类名+订阅方法名+参数类型名,用于快速判等

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (other instanceof SubscriberMethod) {
            checkMethodString();
            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
            otherSubscriberMethod.checkMethodString();
            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false;
        }
    }

    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            StringBuilder builder = new StringBuilder(64);
            builder.append(method.getDeclaringClass().getName());
            builder.append('#').append(method.getName());
            builder.append('(').append(eventType.getName());
            methodString = builder.toString();
        }
    }

    @Override
    public int hashCode() {
        return method.hashCode();
    }
}

SubscriberMethod封装了订阅方法的所有信息,包括订阅方法的Method、入参事件类型、执行在哪种线程、优先级等信息。

查找订阅方法列表

回到注册方法的第二步,我们看看EventBus的核心之一:查找订阅对象上的订阅方法:SubscriberMethodFinder的findSubscriberMethods方法.

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //从缓存中取,METHOD_CACHE的key未订阅对象Class,value为SubscriberMethod列表
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            //通过反射扫描注解得到订阅方法信息
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
            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;
        }
    }

继续看findUsingReflection方法:

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        //FindState用来做订阅方法的校验和保存
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            //向父类遍历
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

//从FindState缓存池中取,取不到再new
private FindState prepareFindState() {
      synchronized (FIND_STATE_POOL) {
          for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
 }

最终会调用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注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        //拿到事件类型
                        Class<?> eventType = parameterTypes[0];
                       //校验是否添加该方法
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //实例化SubscriberMethod对象并添加
                            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");
            }
        }
    }

这里走完,findState里面保存了一个订阅者Class上的所有订阅方法信息,再回到之前的findUsingReflection方法:

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

返回时调用getMethodsAndRelease,经过循环调用后,遍历完subscriberClass和它的父类之后,所有订阅方法信息都保存在findState中,这个方法很显然是从findState取出SubscriberMethod列表返回:

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
       //从findState中取出SubscriberMethod列表
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        //findState复用
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

添加订阅方法校验

在上面的查阅方法中涉及到一个很重要的类FindState,它负责校验一个Method是否可以被注册到总线中去,下面看看它如何校验:

//从执时间类型到方法的映射,用作第一层校验
 final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//从执方法签名到订阅类映射,用作第二层校验,负责根据methodKey覆盖父类订阅方法
 final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
boolean checkAdd(Method method, Class<?> eventType) {
            //两层校验
            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
            // Usually a subscriber doesn't have methods listening to the same event type.
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                 //代码1:事件类型对应的Method第一次加入,表示可以注册到总线
                return true;
            } else {
                //事件类型对应的Method已经存在,则做第二层校验
                if (existing instanceof Method) {
                     //代码2:校验之前的方法,如果不通过则抛异常,这里我不是很理解,校验失败为啥要抛异常,返回false不添加它不可以吗?
                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                        // Paranoia check
                        throw new IllegalStateException();
                    }
                    // Put any non-Method object to "consume" the existing Method
                    //代码3:代码执行到这里,表示之前的Method(existing)校验通过,这里替换原来的value为FindState对象,这个事件类型对应的订阅方法不止一个,之前的Method类型的value被覆盖为FindState类型,表示被覆盖的就Method对象通过第二层校验,如果继续有新Method进来,会直接“下放”到第二层校验,执行代码4即可
                    anyMethodByEventType.put(eventType, this);
                }
                 //代码4:新进来的方法做第二层校验
                return checkAddWithMethodSignature(method, eventType);
            }
        }
        
        //这个方法主要是实现根据方法签名查找对应的所属类,如果待添加的方法的所属类是之前添加过Method所属类的子类(即覆写了它),则替换它,说的这么复杂,其实就是一句话:这个方法的目的就是实现子类同样的订阅方法覆盖父类的订阅方法。
        //校验规则:方法签名methodKey如果如果第一次添加,则返回true,否则只允许子类的订阅方法添加
        private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
           //构建方法签名
            methodKeyBuilder.setLength(0);
            methodKeyBuilder.append(method.getName());
            methodKeyBuilder.append('>').append(eventType.getName());

            String methodKey = methodKeyBuilder.toString();
            //待添加方法所属的类
            Class<?> methodClass = method.getDeclaringClass();
            //之前方法所属的类
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
            if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                //代码5:
                //条件一:第一次添加,条件二:之前的方法所属类为当前方法所属类的父类
                // Only add if not already found in a sub class
                return true;
            } else {
                // Revert the put, old class is further down the class hierarchy
                //代码6:条件都不满足,则回退,保证只有子类的方法才可添加
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }

关于校验这块代码比较绕,我先说明一下校验的原则:
1.Method首次添加,可以注册到总线;
2.对于相同方法名和参数类型的方法,新来的Method如果是子类(或者相同)的订阅方法,则覆盖,可以注册,反之不行;
3.一个订阅者对象可以持有同一个EnventType参数的多个订阅方法,即一个订阅者可以持有多个接收相同事件的方法。
代码注释我尽量解释详细,比较绕。这里我举一个例子来解释校验流程:
假设订阅者持有三个订阅方法,且参数类型相同:M1(type)、M2(type)、M3(type),执行流程
1.M1首次添加,走代码1,返回true;
2.M2添加到anyMethodByEventType,返回M1,走代码2,M1做二层校验通过,anyMethodByEventType中type对应的value为FindState对象state,然后走代码4,M2做二层校验;
3.M3添加到anyMethodByEventType,返回上一步的对象state,走代码4,二层校验M3;
5.M1、M2、M3的方法名不同,所以methodKey也不同,所以都可以通过checkAddWithMethodSignature的二层校验。
PS:上面我们只考虑一个类中的同事件类型的订阅方法,大家考虑下面一种情况:
类B集成类A,A有订阅方法A1(type),A2(type),并且在B类覆写了这俩方法B1(type),B2(type),且加上了订阅注解,大家自己分析完流程,或者写Demo调试一下就可以发现问题,这里是问题描述,希望哪位大神解答!

订阅方法注册到总线

到目前为止我们拿到了一个订阅者上所有的订阅者方法,现在我们需要将它注册到总线当中,回到注册方法:

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //查找订阅者方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        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;
        //构造订阅者,其实就是订阅方法持有者subscriber和subscriberMethod的封装,和subscriberMethod一一对应
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //subscriptionsByEventType的key为事件类型,value为Subscription列表,从事件类型到Subscription一对多映射
       //取Subscription集合
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
       //集合为空则新建,put
        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);
            }
        }
        //按订阅方法优先级插入Subscription列表
        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的key为订阅者subscriber,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);
            }
        }
    }

最后一步注册的大致流程:
1.根据订阅对象subscriber和订阅方法subscriberMethod构造订阅者(Subscription),注意订阅方法和订阅者是一一对应的关系,订阅方法的调用者就是订阅对象,一个订阅对象可以持有多个订阅者;
2.订阅对象按优先级添加到subscriptionsByEventType集合;
3.一个订阅对象只能被注册一次,否则抛出异常;
4.事件类型添加到typesBySubscriber集合,typesBySubscriber是订阅者到事件类型的映射集合,用于注销.
5.如果是粘性事件,由于它的注册往往滞后于发送操作,所以在注册的时候立即提交执行。

事件分发

分发流程

前面经过山路十八弯,分析了完阅方法的收集和注册,终于到了最后步核心功能-事件的提交了,看post方法:

/** Posts the given event to the event bus. */
    public void post(Object event) {
        //1.先拿到PostingThreadState对象
        PostingThreadState postingState = currentPostingThreadState.get();
       //2.取事件队列
        List<Object> eventQueue = postingState.eventQueue;
       //3.添加事件
        eventQueue.add(event);

        if (!postingState.isPosting) {
            //注意:这里postingState.isMainThread标记的post方法执行所在的线程是否在主线程,用于线程切换。
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                    //4循环分发事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

首先是通过currentPostingThreadState.get()方法来得到当前线程PostingThreadState的对象,为什么是说当前线程我们来看看currentPostingThreadState的实现:

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

发现currentPostingThreadState是一个ThreadLocal,也就是所线程私有数据,再继续看看它保存了哪些东西:

 /** For ThreadLocal, much faster to set (and get multiple values). */
    final static class PostingThreadState {
        final List<Object> eventQueue = new ArrayList<>();//当前总线上的事件队列
        boolean isPosting;//是否正在分发事件
        boolean isMainThread;//post方法是否执行在主线程
        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) {//支持事件类型继承,它表示一个子类事件能否响应父类事件的订阅方法。
            //获取到eventClass所有父类的集合,包括eventClass本身
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            //遍历eventTypes集合,分发event事件
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                 //分发事件
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            //分发event事件
            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));
            }
        }
    }

主要是判断是否支持子类事件触发父类事件的订阅方法,如果支持则遍历它的父类集合(包括本事件类)执行单个事件的分发方法;最后都会执行到postSingleEventForEventType方法:

//根据事件类型分发单个事件
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        //订阅者集合
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //取eventClass对应的订阅者集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            //遍历订阅者集合
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                    //分发事件到三种poster,这也是切换线程执行订阅方法的地方
                    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方法做线程切换执行订阅方法,看这个方法之前我们先来了解下订阅方法中的ThreadMode:

/*
 * 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;

/**
 * Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus.
 * EventBus takes care of threading independently from the posting thread.
 * 
 * @see EventBus#register(Object)
 * @author Markus
 */
public enum ThreadMode {
    /**
     * Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,//订阅方法和post方法执行在同一个线程,也就是说不进行线程切换

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
     * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
     * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
     * If not on Android, behaves the same as {@link #POSTING}.
     */
    MAIN,//订阅方法执行在UI线程,如果post执行在UI线程,则立即执行,此时会阻塞post方法,如果post执行在子线程,则订阅方法会加入消息队列,交给MainLooper的Handler处理.

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
     * the event will always be queued for delivery. This ensures that the post call is non-blocking.
     */
    MAIN_ORDERED,//运行在主线程,和MAIN,不同的是,不管post是否运行在主线程,订阅方法都会加入消息队列,交给MainLooper的Handler处理,而不会阻塞post方法。

    /**
     * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
     * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
     */
    BACKGROUND,//运行在后台线程,如果post运行在子线程,订阅方法直接执行,post运行在主线程,订阅方法运行在一个全局后台线程,这个订阅方法加入后台运行队列中,会阻塞其他后台运行的方法,所以需要尽快返回

    /**
     * Subscriber will be called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
     */
    ASYNC //订阅方法执行在单独的线程,异步执行,独立于post线程和主线程,为了避免造成大量的异步线程创建和销毁,采用线程池维护异步线程。
}

//结合注释,ThreadMode的几个类型意义如下:
1.POST:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法;
2.Main:在主线程中执行响应方法,订阅方法不能执行耗时操作,会阻塞运行在主线程的post方法;

  1. MAIN_ORDERED:运行在主线程,和MAIN,不同的是,不管post是否运行在主线程,订阅方法都会加入消息队列,交给MainLooper的Handler处理,而不会阻塞post方法;
  2. BACKGROUND:运行在后台线程,如果post运行在子线程,订阅方法直接执行,post运行在主线程,订阅方法运行在一个全局后台线程,这个订阅方法加入后台运行队列中,会阻塞其他后台运行的方法,所以需要尽快返回;
  3. ASYNC:订阅方法执行在单独的线程,异步执行,独立于post线程和主线程,为了避免造成大量的异步线程创建和销毁,采用线程池维护异步线程.
 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //取订阅方法的执行线程类型
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING://不进行线程切换,和post方法执行在同一线程
                invokeSubscriber(subscription, event);
                break;
            case MAIN://运行在主线程
                if (isMainThread) {//post方法也运行在UI线程,则直接调用
                    invokeSubscriber(subscription, event);
                } else {//post运行在子线程,则提交给HandlerPoster
                    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) {
                     //如果post运行在主线程,则将订阅方法提交到后台任务队列
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    //如果post执行在子线程,则立即执行订阅者方法。
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                //交给异步线程执行
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }
  1. POSTING:不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作;
    2.Main:如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;
  2. MAIN_ORDERED:无论post是否执行在UI线程,订阅方法都会提交到主线程消息队列,不会阻塞post方法;
  3. BACKGROUND:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
    5.Async:不论发布线程是否为主线程,都使用从线程池取一个空闲线程来处理,和Background不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。

总结

EventBus的整体思想是使用了观察者模式将发布者和接收者实现解耦,内部有以下角色:
1.订阅者: Subscription用于订阅方法的调用,内部的subscriber为方法宿主,SubscriberMethod为订阅方法;
2.事件:总线上传递的对象,也是post方法和订阅方法的入参;
3.事件发布者就是EnventBus对象;
整体设计思想描述:
1.根据反射和注解将所有订阅者方法注册到总线,EventBus就持有了某一个事件类型有哪些订阅者,以便事件分发;
2.客户端通过EventBus发布事件event;
3.EventBus内部查找订阅event事件类型的所有订阅者集合;
4.遍历订阅者集合,并按照订阅方法的线程模式分发到Poster上执行订阅方法.
整体的设计类图如下:

EventBus类图
类图摘自[CodeKK的EventBus源代码分析
]
本篇走完了事件注册和分发的流程,限于篇幅,另外一部分重要内容分发器(Poster)和注销方法放在下一篇讲。
上一篇下一篇

猜你喜欢

热点阅读