EventBus源代码解析:1、初始化与订阅者注册

和以前一样,我们同样从我们最经常使用的代码入手,分析EventBus到底是如何进行工作的。所以,第一步,我们先分析EventBus的初始化代码,看看初始化代码当中,都做了哪些工作?

一:初始化

单例模式

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

首先,我们最经常使用的EventBus的默认配置,其实就是一个我们常用的单例模式:

  • 首先尝试获取defaultInstance,如果defaultInstance为null,说明默认的EventBus还没有初始化,因此接下来的工作就是需要对EventBus进行初始化。
  • 此时,为了同一个时刻只有一个EventBus在初始化,也是为了避免多个线程同时访问的时候,各自生成了不同的EventBus对象,因此我们需要对初始化的代码块进行同步。当然同步的范围选择很重要,同步的范围必须是所有的线程都能够同时看到,因此选用了EventBus的类对象作为同步代码块的同步对象。
  • 此时,由于进入到同步代码块之后,可能是由于和其他线程竞争,但没有竞争过,那么此时的时候,很有可能其他线程当中已经将EventBus初始化完成了,所以此时需要再次检查EventBus有没有初始化好,没有的话,说明是第一次进行初始化,那进行初始化工作就可以。
  • 返回唯一的EventBus对象defaultInstance。

具体的初始化工作

构造函数:

public EventBus() {
        this(DEFAULT_BUILDER);
    }

构造函数非常简单,调用了另外的构造函数EventBus(EventBusBuilder builder)实现功能,我们还是需要看看这个构造函数中,都做了哪些工作?

    EventBus(EventBusBuilder builder) {
        // 订阅者们,根据Event类型分类:Key是订阅者的类,Value为对应的订阅者
        subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
        // 订阅者所支持的Event类型
        typesBySubscriber = new HashMap<Object, List<Class<?>>>();
        // 保存Sticky Events,注意,这个地方用的是一个线程安全的HashMap
        stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
        // 主线程的Poster
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        // 后台线程的Poster
        backgroundPoster = new BackgroundPoster(this);
        // 异步的Poster
        asyncPoster = new AsyncPoster(this);
        // 订阅者方法寻找器
        subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }
  • 初始化了以下的队列
    • subscriptionsByEventType:Event的类型作为Key的HashMap,需要注意的是,其中用到了一个CopyOnWriteArrayList,当列表中的元素,其读取次数远远超过写入次数的时候,使用该集合类可以大大提高效率,且该类是线程同步保护的。
    • typesBySubscriber:同样的一个HashMap,保存了某一个订阅者当中所支持的消息类型。有个细节可以注意到,这个地方HashMap的Key是List<Class<?>>,而subscriptionsByEvent中的Key是指定了OnWriteArrayList<Subcription>>,这其中有什么技巧吗?我的理解是这个样子的:
      • 从调用的次数上来考虑,当我们POST一个event的时候,按照我们的常规思路,EventBus就应该根据Event的类型,也就是类去找到那些能够处理这个Event的Subcription,那么这些Subcriptions保存在哪里呢?–>CopyOnWriteArrayList。所以对于这一个列表来说,我们很少回去改动其中的元素,除非有新的Subcription注册。但另外一个,还没想到做什么使用,可能是注销的时候依次注销所注册的订阅者?
    • stickyEvents:保存stickey的Events,需要注意的是,这里面用到的都是ConcurrentHashMap,是线程安全的。
  • 初始化了以下的Poster(后面会依次分析几种Poster的不同实现)
    • mainThreadPoster:通过运行在main线程上的Handler实现
    • backgroundPoster:本质上为一个Runnable
    • asyncPoster:本质上同样为一个Runnable
  • 注册了一个subscriberMethodFinder:即查找订阅者类当中对应的Handler方法
  • 初始化了各种参数:需要注意的是,其中有一个executorService,其默认实现为
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

    后面我们还看到,很多消息的Post都是通过该线程池实现。

上面,我们分析完成EventBus的初始化工作,下面我们继续来分析一下我们在代码中向EventBus注册订阅者的时候,都发生了什么事情?

二、订阅者注册

1、注册:

我们一般情况下,使用的注册代码是:

register(Object subscriber)

跟踪代码,发现该代码实际上通过调用resiter(Object subscriber, boolean sticky, int priority)实现,我们来看看这段代码都做了哪些工作?

private synchronized void register(Object subscriber, boolean sticky, int priority) {
        // 查找订阅类当中的处理方法(包括其父类的方法)
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        // 根据获取的订阅者方法,将其依次订阅
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

上面的代码主要做了两件事情:

  1. 从对应的subscriber类中查找到所有的SubscriberMethod
  2. 依次订阅所有的SubscriberMethod

SubscriberMethod源代码:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        // 获取类的全名作为key
        String key = subscriberClass.getName();
        // 订阅者方法列表
        List<SubscriberMethod> subscriberMethods;
        // 尝试从methodCache中获取订阅者方法
        synchronized (methodCache) {
            subscriberMethods = methodCache.get(key);
        }
        // 如果订阅者方法列表已经存在,则直接返回
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        
        subscriberMethods = new ArrayList<SubscriberMethod>();
        Class<?> clazz = subscriberClass;
        HashSet<String> eventTypesFound = new HashSet<String>();
        StringBuilder methodKeyBuilder = new StringBuilder();
        while (clazz != null) {
            String name = clazz.getName();
            // 如果这些类是java,javax或者android的话,则直接跳过。
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                // Skip system classes, this just degrades performance
                break;
            }

            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
            // 从EventBus2.2开始,强制必须将修饰符设置为public
            Method[] methods = clazz.getDeclaredMethods();
            // 依次遍历类的全部方法
            for (Method method : methods) {
                // 获取方法名
                String methodName = method.getName();
                // 判断方法名是否以ON_EVENT_METHOD_NAME,即是否以"onEvent"开头
                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
                    // 获取方法的修饰符
                    int modifiers = method.getModifiers();
                    // 要求方法匹配public修饰符,并且不是(Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC)中任一种
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                        // 获取参数的类型
                        Class<?>[] parameterTypes = method.getParameterTypes();
                        // 如果参数的类型只有一种,也就是说,参数只有一个
                        if (parameterTypes.length == 1) {
                            // 获取方法名当中去掉onEvent剩下的部分,并根据这一部分判断其工作在哪个县城之上
                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
                            ThreadMode threadMode;
                            if (modifierString.length() == 0) {
                                // PostThread
                                threadMode = ThreadMode.PostThread;
                            } else if (modifierString.equals("MainThread")) {
                                // MainThread
                                threadMode = ThreadMode.MainThread;
                            } else if (modifierString.equals("BackgroundThread")) {
                                // BackgroundThread
                                threadMode = ThreadMode.BackgroundThread;
                            } else if (modifierString.equals("Async")) {
                                // AsyncThread
                                threadMode = ThreadMode.Async;
                            } else {
                                if (skipMethodVerificationForClasses.containsKey(clazz)) {
                                    continue;
                                } else {
                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);
                                }
                            }
                            // 获取订阅者处理方法onEvent方法的参数类型
                            Class<?> eventType = parameterTypes[0];
                            methodKeyBuilder.setLength(0);
                            // 添加方法名
                            methodKeyBuilder.append(methodName);
                            // 添加方法类型
                            methodKeyBuilder.append('>').append(eventType.getName());
                            String methodKey = methodKeyBuilder.toString();
                            // 将methodKey添加到eventTypesFound当中 true:说明该方法没有被添加过,false:说明该方法已经被添加过
                            if (eventTypesFound.add(methodKey)) {
                                // Only add if not already found in a sub class
                                // 如果methodKey在子类当中没有被添加过,则构造SubscriberMethod,并添加到subscriberMethods当中
                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                            }
                        }
                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
                                + methodName);
                    }
                }
            }
            // 该类处理完成,继续处理其父类!!!
            clazz = clazz.getSuperclass();
        }
        // 如果该类或者其父类当中不包含订阅者方法,那么则抛出异常
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            // 该类或者其父类当中,包含订阅者方法,则将其添加到methodCache当中
            synchronized (methodCache) {
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }

SubscriberMethod方法的程序流程图:

findSubscriberMethods

 

 

简单的理解, findSubscriberMethods的工作就依次遍历指定类中的所有方法,从中找到EventBus的handler并将其添加到subscriberMethods集合当中。具体的步骤如下:

  1. 首先判断clazz类以否是java/javax/android的类,如果是,则说明这个类并不是我们的订阅者(可能是订阅者的父类,不需要处理)。
  2. 然后遍历类中所有的方法,首先挑选出其中以onEvent开头的方法,继续判断是否是我们的Event处理方法。
  3. 判断该方法的修饰符,要求方法匹配public修饰符,并且不是(Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC)中任一种。
  4. 获取onEvent方法的参数类型,即要处理的Event的类型。
  5. 构建SubscriberMethod对象,并添加到subscriberMethods
  6. 获取clazz的父类,重复步骤1-6

subscribe代码如下:

    // Must be called in synchronized block
    // 必须要在同步快中执行
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
        
        // 获取订阅者处理方法的参数类型,即Event事件的类型
        Class<?> eventType = subscriberMethod.eventType;
        // 根据类型尝试从subscriptionsByEventType中获取参数类型,即Event事件类型所对应的订阅者列表。
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        // 生成新的订阅者对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
        // 如果订阅者列表为null,则说明是头一次添加该Event类型。
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<Subscription>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        // subscriberMethod.method.setAccessible(true);
        // 从EventBus2.2之后,设置方法必须为public
        // subscriberMethod.method.setAccessible(true);        
        int size = subscriptions.size();
        // 根据优先级,将包含订阅者处理方法的订阅者对象添加到队列的合适位置上
        for (int i = 0; i <= size; i++) {
            if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        // 保存订阅者类所能够处理的EventType
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<Class<?>>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        // 如果Event设置为sticky
        if (sticky) {
            Object stickyEvent;
            synchronized (stickyEvents) {
                stickyEvent = stickyEvents.get(eventType);
            }
            if (stickyEvent != null) {
                // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
                // --> Strange corner case, which we don't take care of here.
                postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
            }
        }
    }

其程序架构图如下:

subscribe

 

这个其实理解起来比较简单,主要分了下面几个步骤:

  1. 根据订阅者所在的类,订阅者的方法,生成相应的订阅者对象,即Subscription对象。
  2. 获取Event的事件类型,查看此事件是否有相应的List,如果有,则说明之前有注册过其他的可以处理该事件类型的Subcription,那么则根据优先级将此次的Subcription对象插入到合适的位置,否则则新建List,并将Subcription对象插入进来。

 

About: happyhls


发表评论

电子邮件地址不会被公开。 必填项已用*标注