美文网首页
EventBus3.0源码分析(一)事件注册与发布流程

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

作者: kakaxicm | 来源:发表于2018-07-18 14:35 被阅读0次

    引言

    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)和注销方法放在下一篇讲。

    相关文章

      网友评论

          本文标题:EventBus3.0源码分析(一)事件注册与发布流程

          本文链接:https://www.haomeiwen.com/subject/geqwpftx.html