EventBus 源码分析 - 注解 + 反射

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 注解的方法找出来,并将它们封装成 List<SubscriberMethod>SubscriberMethod 的成员变量如下所示:

image.png

我们来看下 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,如下:

image.png

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

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

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

第一部分小结

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

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

findSubscriberMethods 方法完成后图解.png

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<Subscription>(线程安全的 ArrayList,里面是 Subscription)

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

第二部分小结

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

subscribe 完成后图解.png

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学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值