EventBus源码再分析
一、概述
EventBus是一个开源的用于Android和Java上的一个:订阅--->发布事件总线。
优点:
1.只要是在一个JVM内,就可以实现通信
2.小巧灵活、不占内存
3.解耦,切换线程灵活
4.库小,不占内存
缺点:
1.注册和反注册时一对,如果忘记了就会出现内存泄漏
2.拿到注解方法组是通过反射来进行的,效率稍低
3.在整个项目中需要定义不少的Event组件,事件如果很多就会看起来比较臃肿
ps:就目前来说,如果项目中使用kotlin,那么事件总线可以使用SharedFlow或者StateFlow来代替。
二、原理分析
从使用上来说EventBus就三板斧。注册:EventBus.getDefault().register(obj)、取消注册:EventBus.getDefault().unregister(obj)、发送事件:EventBus.getDefault().post(event)、接收事件@Subscribe(threadMode=ThreadMode.MAIN)接收事件
一、先说注册
public void register(Object subscriber) { //获取注册对象的class字节码对象 Class<?> subscriberClass = subscriber.getClass();
//这一步比较关键,作用是用来获取subscriber对象中包含@Subscribe注解的方法集合 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) {
//循环对方法集合进行注册,其实就是往集合中去添加找到的方法名。 subscribe(subscriber, subscriberMethod); } } }
接下来看看subscriberMethodFinder.findSubscriberMethods(subscriberClass)方法中都干了啥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {<br> //先判断之前装载了没有,如果装载了就直接拿来使用,不用继续往下执行,如果没有装载则往下执行 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; } } |
接下来看:subscriberMethods = findUsingReflection(subscriberClass);//拿到注解方法集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null ) { findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); } return getMethodsAndRelease(findState); } 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) {<br> //获取方法上的@Subscribe注解 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe. class ); if (subscribeAnnotation != null ) { //如果Subscribe对象不为空,说明这个方法上有@Subscribe注解 Class<?> eventType = parameterTypes[0]; //拿到第一个参数,用于确定事件类型 if (findState.checkAdd(method, eventType)) {<br> //获取与这个注解相关联的线程模型 ThreadMode threadMode = subscribeAnnotation.threadMode();<br> //将这个注解的信息存入集合,包括:方法对象、事件类型、线程模型等 findState.subscriberMethods.add( new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe. class )) { .... } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe. class )) { .... } } } |
到这里:subscriberMethodFinder.findSubscriberMethods(subscriberClass);获取注解方法列表的方法已经返回了方法列表,算是走完了。
下面看下循环注册subscribe(subscriber, subscriberMethod);方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class<?> eventType = subscriberMethod.eventType;<br> //将注册对象和注册对象中的@Subscribe注解信息封装成为一个Subscription,方便后面使用 Subscription newSubscription = new Subscription(subscriber, subscriberMethod);<br> //先看看以前有没有存过,如果没有就new一个 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType. get (eventType); if (subscriptions == null ) { subscriptions = new CopyOnWriteArrayList<>();<br> //把事件类型和时间类型对应的对象及对象注解信息存入map,这个后面unRegister的时候也会用的到 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) {<br> //把封装好的注册对象和对象包含的注解方法信息存入集合 subscriptions.add(i, newSubscription); break ; } } List<Class<?>> subscribedEvents = typesBySubscriber. get (subscriber); if (subscribedEvents == null ) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); //把事件类型存起来,放入typesBySubscriberd对象中,后面会用到 ..... } |
到这里注册就分析完了,其实整体上看起来是比较简单的,流程无非是:拿到订阅对象,通过订阅对象拿到对应的class。在运行时拿到class的所有方法,过滤出带有@subscribe注解的方法,并发放发信息存入对象,对象存入集合,然后返回。通过拿到返回的对象集合,循环的去进行注册,其实就是把对象方法,和事件类型封装到map中,供后面使用。
二、下面说说EventBus.getDefault.Post(event)方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public void post(Object event ) {<br> //事件入队列 PostingThreadState postingState = currentPostingThreadState. get (); List<Object> eventQueue = postingState.eventQueue; eventQueue.add( event ); if (!postingState.isPosting) { postingState.isMainThread = isMainThread(); //判断post所在的线程是主线程还是子线程,并记录下来 postingState.isPosting = true ; .... try { while (!eventQueue.isEmpty()) {<br> //分发事件 postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false ; postingState.isMainThread = false ; } } } |
下面看看postSingleEvent都干了什么事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | private void postSingleEvent(Object event , PostingThreadState postingState) throws Error { Class<?> eventClass = event .getClass(); //依然是通过时间对象获取其字节码对象 boolean subscriptionFound = false ; ...... subscriptionFound = postSingleEventForEventType( event , postingState, eventClass); //接着对事件分发 ..... } private boolean postSingleEventForEventType(Object event , PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized ( this ) { subscriptions = subscriptionsByEventType. get (eventClass); //这句话很重要,表示通过事件类型在subscriptionsByEventType的map中拿,@Subscribe对象中此注解的方法信息集合 } if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { //@Subscribe信息集合拿到后开始遍历 postingState. event = event ; postingState.subscription = subscription; boolean aborted; try { postToSubscription(subscription, event , postingState.isMainThread); //分发并执行方法 aborted = postingState.canceled; } finally { ..... } return true ; } return false ; } private void postToSubscription(Subscription subscription, Object event , boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case POSTING: //表示默认线程,post在哪里调用,次方发就在哪里执行 invokeSubscriber(subscription, event ); //这个方法表示通过java的反射机制,通过method.invoke来执行方法,其实就是主动调用 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); } } |
我们看下invokeSubscriber方法中时什么东西
1 2 3 4 5 | void invokeSubscriber(Subscription subscription, Object event ) { try {<br> //从对象中拿到注解方法对象,然后调用方法对象的invoke,把参数传递进去主动执行 subscription.subscriberMethod.method.invoke(subscription.subscriber, event ); } <br> .... } |
到这里post方法算是执行完事了。其实Post方法说是发送事件倒不如说是,主动执行事件。我们可以看下它所做的操作:1.根据事件类型,拿到注解方法集合、2.遍历集合、3.遍历后拿到@Subscribe注解标注的方法对象,然后执行method.invoke方法。这样看起来就是就是主动执行。
三、最后看看EventBus.getDefault().unregister(obj)干了啥
1 2 3 4 5 6 7 8 9 10 11 | public synchronized void unregister(Object subscriber) {<br> //根据对象拿到类型集合,这是register的时候存好的 List<Class<?>> subscribedTypes = typesBySubscriber. get (subscriber); if (subscribedTypes != null ) { for (Class<?> eventType : subscribedTypes) {<br> //循环遍历取消注册 unsubscribeByEventType(subscriber, eventType); } typesBySubscriber.remove(subscriber); //取消注册的时候一定要移除 } else { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } } |
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//注册的时候才存好的结合,根据事件类型拿到Subscription集合 List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions != null) { int size = subscriptions.size(); for (int i = 0; i < size; i++) { Subscription subscription = subscriptions.get(i); if (subscription.subscriber == subscriber) {//遍历出自己,然后移除就行了 subscription.active = false; subscriptions.remove(i); i--; size--; } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
2013-08-21 Android 导入工程文件引用包出错