EventBus使用及源码分析(一)

371 阅读5分钟

一 在项目中使用EventBus

首先是在项目中导入EventBus,最新版本3.1.1(更新于2017年)

implementation 'org.greenrobot:eventbus:3.1.1'

在Activity中,一般是在onStart()方法中register EventBus,在onStop()中unregister。 有些项目中,想要Activity在后台时,也可以订阅,可以在onCreate(),onDestroy()中register,unregister。

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

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

EventBus在这里使用了一个单例模式,通过getDefault()获取单例对象。

后面我们设置两个ImageView的点击事件,通过post方法发送不同的Event。(其中的两个Event不在这里贴代码,其中主要是有一个String变量保存信息)

@Override
public void onClick(View v) {
 switch (v.getId()) {
    case R.id.send0:
        Send0ClickEvent msg = new Send0ClickEvent();
        msg.setMsg("Hello World");
        EventBus.getDefault().post(msg);
        break;
    case R.id.send1:
        Send1ClickEvent msg1 = new Send1ClickEvent();
        msg1.setMsg("你好 世界");
        EventBus.getDefault().post(msg1);
    default:
        break;
 }
}

再写订阅的方法。

 @Subscribe(threadMode = ThreadMode.MAIN)
 public void onClickSend0Event(Send0ClickEvent event){
    Toast.makeText(this, "onClickSend0Event " + event.getMsg(), Toast.LENGTH_SHORT).show();
 }

 @Subscribe(threadMode = ThreadMode.MAIN)
 public void onClickSend1Event(Send1ClickEvent event){
    Toast.makeText(this, "onClickSend1Event " + event.getMsg(), Toast.LENGTH_SHORT).show();
 }

最终,点击send0和send1会分别postEvent,通过Event Type (如上面代码中的Send0ClickEvent和Send1ClickEvent这两种类型的Event)分发给不同的订阅方法。

二 分析EventBus的源码

1.register()

/**
 * 注册订阅者接受事件,register()之后,当不需要订阅时必须要调用unregister()
 * 订阅者中必须有方法使用@Subscribe注释修饰,在注释中可以配置ThreadMode和priority
 */
public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

通过findSubscriberMethods获取订阅者中使用了@Subscribe的方法。下面看看findSubscriberMethods的代码。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //METHOD_CACHE是一个全局缓存。
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
        //在这里将获取的subscriberMethods缓存到内存中。
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

首先在METHOD_CACHE中查询有没有已经保存的subscriberMethods,有就直接返回。所以,一般只有第一次注册是比较花费时间的。后面判断ignoreGeneratedIndex,如果没设置过,默认是false,调用了findUsingInfo,是在后面使用getDeclaredMethods/getMethod来遍历这个类的所有方法。如果设置了编译期获取订阅者信息,会调用findUsingReflection。在这里只分析findUsingInfo,下面是代码。

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    //在EventBus中会维护一个FindState数组。prepareFindState只是从FindState数组中拿一个出来用。
    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);
        }
        //在这里就可以知道while (findState.clazz != null)的意思了,所有的父类都会执行一次上面的代码。
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}

首先获取一个FindState对象,用来存储后面获取到的结果,在这里FindState是循环使用的,每次获取到结果后会重置。后面的findState.subscriberInfo没设置过的话为null,所以后面是调用到findUsingReflectionInSingleClass方法。

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {getMethods
        // 像Activity这种很多方法的Class,getDeclaredMethods方法比getMethods更快。
        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方法,且不能是abstract static等修饰
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            //必须只有一个入参
            if (parameterTypes.length == 1) {
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                //必须使用@Subscribe注释
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException("@Subscribe method " + methodName +
                        "must have exactly 1 parameter but has " + parameterTypes.length);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

在上面代码中,获取订阅者中符合条件的方法,最终将符合的方法都存储在findState中(包括了方法 event类型 注释中包含的信息)。 最终获取到了调用register()方法时传入的class参数中使用了@Subscribe注释的方法。

再回看register的代码

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方法。然后我们看看这个方法是做了什么。

// 必须在synchronized代码块中调用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    //subscription中包含了订阅者这个对象的引用、和订阅的方法。
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //从subscriptionsByEventType这个map中获取subscriptions,map的key为eventType。
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        //注释中如果有给priority赋值,则会在这里起作用,这里priority大的优先级高,会在List的前面。如果优先级相等,则按照先后顺序。
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    //typesBySubscriber也是一个map,key也是订阅者这个对象的引用,value是这个订阅者中所有的eventType。
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    //sticky这个值是调用postSticky()方法才会为true。postSticky()方法与post()方法类似,唯一不同是可以在register前调用,然后会将post的事件暂时存储起来,
    //在register时(也就是这里)再去执行。
    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);
        }
    }
}

这个方法将前面获取的methods信息都放到两个map中,subscriptionsByEventType这个map的key是eventType,value是subscrition列表(一个subscrition包含了订阅者,以及一个订阅方法这些信息),typesBySubscriber这个map的key是订阅者,value是这个订阅者的所有订阅的eventType。最后还有关于sticky的代码,在这里不过多赘述。

至此register的代码分析完了,主要就是将订阅者传进来,然后根据class获得订阅的方法,最后将这些信息都存储到两个全局map中。