EventBus 源码分析 - 注解 + 反射

作者:萌果爱吃柠檬
转载地址:https://juejin.cn/post/7096415729934827551

EventBus 源码解析

随着 LiveDataKotlin Flow 的出现,EventBus 已经慢慢过时了。不过 EventBus 源码的设计思想以及实现原理还是值得我们去学习的。

getDefault() 方法

EventBus().getDefault().register(this)

首先 EventBus 的创建用到了 DCL 单例模式,源码如下:

public class EventBus {

    static volatile EventBus defaultInstance;

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

register() 方法第一部分

示例:

public class MainActivity extends AppCompatActivity {

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

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

    /**
     * threadMode 执行的线程方式
     * priority 执行的优先级
     * sticky 粘性事件
     */
    @Subscribe(threadMode = ThreadMode.MAIN,priority = 50,sticky = true)
    public void test1(String msg){
        // 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
        Log.e("TAG","msg1 = "+msg);
    }

    @Subscribe(threadMode = ThreadMode.MAIN,priority = 100,sticky = true)
    public void test2(String msg){
        // 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
        Log.e("TAG","msg2 = "+msg);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

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

EventBus().getDefault().post("text")

register() 方法源码:

public void register(Object subscriber) {

    // 获取 class 对象
    Class<?> subscriberClass = subscriber.getClass();

    // 遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

这里的 subscriber 就是我们的 MainActivity,第一行代码很简单,就是获取 MainActivityclass 对象。

第二行代码的意思:遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来,并将它们封装成 ListSubscriberMethod 的成员变量如下所示:

EventBus 源码分析 - 注解 + 反射_第1张图片

我们来看下 findSubscriberMethods 源码:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {

    // 先从缓存里面读取
    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 {
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

这里的 subscriberClass 就是我们的 MainActivityclass 对象。

ignoreGeneratedIndex 这个属性表示是否忽略注解成器生成的 EventBusIndex.class 文件, 我们只要知道 ignoreGeneratedIndex 默认是 false 就行了。

所以会走 findUsingInfo(subscriberClass) 方法,我们来看下源码:

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

这里我直接说结论,因为我们没有使用编译时注解生成 EventBusIndex.class 文件,所以 findState.subscriberInfo 为空,所以这里会走 findUsingReflectionInSingleClass(findState) 方法,最终就是通过反射去找。我们来看下 findUsingReflectionInSingleClass 源码:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
       ......
        }
    }
    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 subscribeAnnotation = method.getAnnotation(Subscribe.class);
                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()));
                    }
                }
            } 
        } 
    }
}

第一行代码 findState.clazz.getDeclaredMethods(); 就是通过反射获取订阅类的所有方法。

然后遍历 methods,判断方法的修饰符是不是 public,然后获取方法参数的 class,并判断是不是只有一个参数,并且还判断是否被 @Subscribe 修饰。method.getParameterTypes() 就是获取方法的参数类型。String msg 获取到的就是 String.class,如下:

EventBus 源码分析 - 注解 + 反射_第2张图片

遍历完 methods 之后,就会将所获得的属性添加到 subscriberMethods 中,如下:

findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));

最后通过 findStatesubscriberMethods 返回回去。也就是 register() 方法中的List

第一部分小结

public void register(Object subscriber) {

    // 获取 class 对象
    Class subscriberClass = subscriber.getClass();

    // 遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来
    List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

subscriberMethodFinder.findSubscriberMethods() 方法就是通过反射去解析注册者(MainActivity)的所有方法。并且找出被 @Subscribe 注解修饰的方法,然后通过 Annotation 解析所有需要的参数(包括 threadModeprioritysticky、参数的类型 eventTypemethod)。最后把这些参数封装成 SubscriberMethod 添加到集合返回。

EventBus 源码分析 - 注解 + 反射_第3张图片

register() 方法第二部分

接下来我们来分析 subscribe 方法:

synchronized (this) {
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        subscribe(subscriber, subscriberMethod);
    }
}

我们先看一下 subscribe() 方法的参数,subscriber 参数就是我们的 MainActivitysubscriberMethod 参数是我们上面对方法的封装。

我们来看一下 subscribe() 方法的源码:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // eventType 是方法参数的 class,也就是 String.class
    Class<?> eventType = subscriberMethod.eventType;

    // 又封装了一个 Subscription 对象 subscriber : MainActivity, subscriberMethod : 解析好的方法
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

    // 第一次拿为空,put
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        ......
    }

    // 优先级排序 
    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;
        }
    }

    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    // 粘性事件的处理
    ```
}

这里又封装了一个 Subscription 对象,成员变量就是:

  • subscriber 参数就是我们的 MainActivity
  • subscriberMethod 参数是我们上面对方法的封装

接下来用 subscriptionsByEventTypeSubscription 对象存起来,subscriptionsByEventType 是个 Map,如下:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
  • key : Class(参数类型,也就是 Strin.class)
  • value : CopyOnWriteArrayList(线程安全的 ArrayList,里面是 Subscription)

subscriptionsByEventType.get(eventType) 第一次拿肯定为空,所以会调用 subscriptionsByEventType.put(eventType, subscriptions)。到这里,subscriber 方法就分析好了。

第二部分小结

解析所有的 subscriberMethodeventType,然后将它们封装成 Map, CopyOnWriteArrayList> 类型的 subscriptionsByEventTypekeyeventTypevalueSubscription 的列表,Subscription 包含两个属性 subscribersubscriberMethod

EventBus 源码分析 - 注解 + 反射_第4张图片

post() 方法

其实根据上面那个图,我们很容易猜出 post() 方法肯定会遍历 subscriptionsByEventType,找到一样的 eventType,然后去执行 CopyOnWriteArrayList 列表里面的 Subscription 对象里面的 subscriberMethod 对象里面的 method

我们来看下 post() 方法源码:(经过一系列调用)

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

这里的 event 就是 post("text") 里面的 text,所以 eventClass 就是 String.class,然后执行 subscriptions = subscriptionsByEventType.get(eventClass),就会将我们上面图片中的 value 取出来。然后遍历 subscriptions,执行 postToSubscription(subscription, event, postingState.isMainThread) 方法,我们看下这个方法的源码:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                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) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

我们的 threadMode 有四种形式:

  • POSTING:同一个线程,在哪个线程发送,就在哪个线程执行。
  • MAIN:在主线中执行。
  • BACKGROUND:子线程,如果发送事件是主线程,那么久调用线程池切换线程来执行方法。
  • ASYNC:异步线程,无论发布时间是主线程还是子线程,都有通过线程池切换线程执行。

所以我们看到源码中,如果是 POSTING 方式就直接调用 invokeSubscriber(subscription, event) 执行方法。其他方式就可能需要调用线程池切换线程来执行订阅方法。

invokeSubscriber 源码如下:

void invokeSubscriber(Subscription subscription, Object event) {
    try {
      //  subscriber 是 MainActivity,event 是 post 发送的 "text"
      subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e);
    }
}

post() 方法小结

post() 方法会将 post(Object event) 里的 eventclass 对象作为 key,去 subscriptionsByEventType 这个 map 里面取出 keyeventclass 相同的 subscriptions,然后遍历 subscriptions去执行订阅方法。

unRegister()

unRegister() 方法就是将上面所产生的那些 map 清空

总结

EventBus 执行订阅方法的原理主要是通过注解反射。细节上用的的知识还是非常多的,比如 DCL 单例模式Builder 设计模式享元设计模式FindState 的对象池设计)、线程池HandlerThreadLocal 等。

并且还引入了注解处理器,使用这种方式可以不需要用到反射来查找订阅方法,优化了运行时性能。不过本文的源码分析分析的还是利用反射来查找订阅方法。

通过阅读 EventBus 源码,可以看出作者很多地方的精彩设计,很值得我们学习。本文只是分析了核心的 EventBus 源码,很多细节上的源码就直接跳过去了。

你可能感兴趣的:(Android开发,移动开发,android成长路劲,java,android,移动开发,经验分享,框架)