可能你的EventBus使用并不正确,是时候真正搞懂EventBus了(上)

EventBus

    • 1、EventBus使用
    • 2、常规原理分析
      • 2-1、`EventBus.getDefault().register(Object subscriber)`
        • 2-1-1、`subscriberMethodFinder#findSubscriberMethods`
        • 2-1-2、`subscribe(subscriber, subscriberMethod)`
      • 2-2、`unregister(Object subscriber)`

EventBus作为Android开发中使用频率相当高的项目,帮助我们简化了很多复杂场景下的数据传递,当然也是Android初中高级面试中必不可少的问题。

但是对于EventBus使用,可能绝大多数人并没有正确的使用它,今天就让我来带大家正确的使用它,并带大家从源代码层面真正搞懂EventBus

EventBus项目地址: https://github.com/greenrobot/EventBus

可能你的EventBus使用并不正确,是时候真正搞懂EventBus了(上)_第1张图片

1、EventBus使用

第一步:注册

//注册
EventBus.getDefault().register(this)
//反注册
EventBus.getDefault().unregister(this)

注册和反注册操作,需要注意使用确保生命周期步调一致,比如你在onResume中注册,就应该在onPause中反注册。

第二步:设置方法,并添加注解

@Subscribe(threadMode = ThreadMode.MAIN)
fun onReceiverEventTwoNormal(event: FragmentEvent) {
     
    if (event.isOne()) {
     
        bindView {
     
            tvReceiver.text = "接收到的内容:${
       event.eventMsg}"
        }
    }
}

第三步:使用post方法事件

EventBus.getDefault().post(FragmentEvent.postOne())

到这里,相信和大家使用的也都是一致的,我见过的大部分项目也都是这么设置的。

但是如果只说这些的话,相信我也就不需要写这篇文章了。

你面试有没有遇到过这样的问题呢?

req: EventBus 2.x和eventbus 3.x有什么区别?

相信很多背过面试题的肯定能脱口而出,EventBus2.x实现使用反射,而3.0是使用注解方法,并加入了索引进行了优化。好~,看起来好像很perferct,但是你索引是怎么实现的?而且上面的代码真的是相比2.0代码性能更优化了吗?

如果我告诉你,上面的处理方式,性能相比EventBus 2.x 更差,不知道你作何感想。直接上图。

可能你的EventBus使用并不正确,是时候真正搞懂EventBus了(上)_第2张图片
这里先放一下正确的使用方式,后面分析完源代码相信大家也就理解了。

在项目或者模块下的build.gradle文件中添加:

kapt {
    arguments {
        arg('eventBusIndex', 'org.fireking.event.MyEventBusIndex')
    }
}
dependencies{
	kapt 'org.greenrobot:eventbus-annotation-processor:3.0.0'
	...
}

重新rebuild一下代码,会在build/generated/source/kapt/debug/下生成org.fireking.event.MyEventBusIndex文件,打开之后发现其实就是我们使用subscribe注解标识过的类。

...
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

static {
     
    SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

    putIndex(new SimpleSubscriberInfo(org.fireking.laboratory.eventbus.EventBusOneFragment.class, true,
            new SubscriberMethodInfo[] {
     
        new SubscriberMethodInfo("onReceiverEventTwoNormal", org.fireking.laboratory.eventbus.FragmentEvent.class,
                ThreadMode.MAIN),
    }));

    putIndex(new SimpleSubscriberInfo(org.fireking.laboratory.eventbus.EventBusTwoFragment.class, true,
            new SubscriberMethodInfo[] {
     
        new SubscriberMethodInfo("onReceiverEventTwoNormal", org.fireking.laboratory.eventbus.FragmentEvent.class,
                ThreadMode.MAIN),
    }));

}
...

聪明的你肯定想到了,这里是直接使用kapt,将之前注解的类和方法关系直接生成文件保存在map中了,以后使用应该直接就可以从这里面取了,不再需要反射查找了。至于具体是不是这样,后面源码会说明。

在application中,新增设置

//将上面生成的方法注册到默认的eventBus配置项中,后续使用就可以和原来一样直接用了。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

2、常规原理分析

对于Event实现分析,我们直接根据常用的方法流程进行分析。

2-1、EventBus.getDefault().register(Object subscriber)

public void register(Object subscriber) {
     
	//根据当前传入的对象,找到类
   Class<?> subscriberClass = subscriber.getClass();
   //根据当前类对象找到该类对象下的所有订阅方法SubscriberMethod
    List<SubscriberMethod> subscriberMethods
    =subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
     
    	//遍历所有的订阅方法
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
     
            subscribe(subscriber, subscriberMethod);
        }
    }
}

2-1-1、subscriberMethodFinder#findSubscriberMethods

//用来保存类和类里面所有订阅方法关系的集合
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE 
= new ConcurrentHashMap<>();

...
//SubscriberMethod是保存的使用Subscriber注解的方法信息
public class SubscriberMethod {
     
    //使用Subscriber注解的方法
    final Method method;
    //对应注解中的threadMode
    final ThreadMode threadMode;
    //对应方法传递的参数,EventBus限制只要一个参数,这里对应的就是第一个参数
    final Class<?> eventType;
    //事件优先级
    final int priority;
    //是否粘性
    final boolean sticky;
    ...
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
     
		//从Method-Cachae中查找是否已经存在缓存数据
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
		//如果找到已经缓存过,直接返回
        if (subscriberMethods != null) {
     
            return subscriberMethods;
        }
		//ignoreGeneratedIndex 用来区分是否忽略Annotation Processor生成的索引
		//如果忽略的话,则默认会使用反射方式,否则使用生成的索引方式查找
        if (ignoreGeneratedIndex) {
     
        	//使用反射方式查找
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
     
        	//使用索引方式查找,如果索引内查找不到,则使用反射再次查找
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        //如果设置了EventBus.getDefault().register(),
        //但是类里面没有使用@Subscriber的方法,则会抛出异常
        if (subscriberMethods.isEmpty()) {
     
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
     
        	//检索完成之后,将类和@Subscriber方法关系保存在Method-Cacahe中
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            //返回subscriberMethods
            return subscriberMethods;
        }
    }
  • private List findUsingReflection(Class subscriberClass) 反射方式
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
     
    //从大小为4的缓存池中获取FindState对象
    FindState findState = prepareFindState();
    //初始化FindState对象状态
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
     
        //使用反射方式查找
        findUsingReflectionInSingleClass(findState);
        //从父类查找
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}

核心查找方法findUsingReflectionInSingleClass

private void findUsingReflectionInSingleClass(FindState findState) {
     
    Method[] methods;
    try {
     
        //只查找公有方法,以增加查找速度
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
     
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        try {
     
            methods = findState.clazz.getMethods();
        } catch (LinkageError error) {
      // super class of NoClassDefFoundError to be a bit more broad...
            String msg = "Could not inspect methods of " + findState.clazz.getName();
            if (ignoreGeneratedIndex) {
     
                msg += ". Please consider using EventBus annotation processor to avoid reflection.";
            } else {
     
                msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
            }
            throw new EventBusException(msg, error);
        }
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
     
        //获取方法修饰符 什么都不加 是0 , public  是1 ,private 是 2 ,protected 是 4,static 是 8 ,final 是 16。
        int modifiers = method.getModifiers();
        //修饰符 为public
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
     
            //获取参数的类对象类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            //eventBus的参数只能使用一个,超过一个则抛出异常
            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中
                        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");
        }
    }
}

父类查找,可以看之前代码,这里会将clazz方法替换为父类对象,继续在while循环中使用`findUsingReflectionInSingleClass`和`moveToSuperclass`进行逐层查找。
```java
 void moveToSuperclass() {
     
     //如果跳过查找父类,返回null
     if (skipSuperClasses) {
     
         clazz = null;
     } else {
     
         //返回父类
         clazz = clazz.getSuperclass();
         String clazzName = clazz.getName();
         // Skip system classes, this degrades performance.
         // Also we might avoid some ClassNotFoundException (see FAQ for background).
         //跳过java和android类
         if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||
                 clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
     
             clazz = null;
         }
     }
 }
}

最后返回查找到的方法集合,并清除缓存池getMethodsAndRelease

`findState`缓存池在使用时候,需要释放,以供其他操作使用
```java
private List getMethodsAndRelease(FindState findState) {
    List subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    //查找完成之后释放缓存池
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

因为查找方式有两种,分别是反射方式和apt方式,接下来会讲解apt方式,至于查找到时候使用方式,下面也会做说明。

  • private List findUsingInfo(Class subscriberClass) 优先apt方式,查找不到会反射查找
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
     
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
     
    	//从apt生成的索引信息中查找
        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);
}
  • getSubscriberInfo
private SubscriberInfo getSubscriberInfo(FindState findState) {
     
    if (findState.subscriberInfo != null && 
    findState.subscriberInfo.getSuperSubscriberInfo() != null) {
     
    	//在父类中查找
        SubscriberInfo superclassInfo = 
        findState.subscriberInfo.getSuperSubscriberInfo();
        //如果找到,则直接返回
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
     
            return superclassInfo;
        }
    }
    //SubscriberInfoIndex 是一个接口,我们使用apt生成的类,就是继承自它
    if (subscriberInfoIndexes != null) {
     
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
     
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            //如果从apt生成的索引中找到,则直接返回
            if (info != null) {
     
                return info;
            }
        }
    }
    return null;
}

2-1-2、subscribe(subscriber, subscriberMethod)

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
     
     //得到@Subscribe的方法参数
     Class<?> eventType = subscriberMethod.eventType;
     //这里构造出一个Subscription对象,该对象是对订阅的类和类里面的@Subscribe方法的一个封装,将这两个封装到了一个类里面
     Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
     //subscriptionsByEventType是一个以@Subscribe方法参数作为key,订阅方法集合作为value的Map
     CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
     //检查是否已经注册过,重复注册抛出异常
     if (subscriptions == null) {
     
         //这里使用的是一个线程安全的读写队列
         subscriptions = new CopyOnWriteArrayList<>();
         //如果没有注册过,则将改eventType的所有方法,统计到一个集合中
         //这里的eventType是@Subscribe的参数,所以可以理解为将所有注册了改类型的放在一个集合中,
         // 在unRegister的时候,会从集合中移除,所以可以实现通知到所有已经注册的方法的功能
         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++) {
     
         //根据优先级进行一个排序,因为每次都会比较优先级,
         // 所以最终是形成了一个优先级从高->低的排序
         if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
     
             subscriptions.add(i, newSubscription);
             break;
         }
     }

     //其实和subscriptionsByEventType类型的一个集合,
     // 发现只是用来协助isRegistered方法判断是否已经注册过
     List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
     if (subscribedEvents == null) {
     
         subscribedEvents = new ArrayList<>();
         typesBySubscriber.put(subscriber, subscribedEvents);
     }
     subscribedEvents.add(eventType);

	//这里是对于粘性事件处理,后面专门说明
     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).
             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);
         }
     }
 }

下面简单总结下EventBus#register(Object subscriber)的工作流程

使用subscriberMethodFinder.findSubscriberMethods,以注册参数subscriber作为参数查询是该类里面的所有使用了@Subscribe注解的方法。

查找逻辑如下:

如果在METHOD_CACHE中找到,则直接返回。找不到的话,则判断是否使用apt方式在编译期生成,如果没有使用apt方式,则使用反射方式查找。

在查找到之后,则保存在METHOD_CACHE中,并返回后续使用。

之后对于查找到的@Subscribe注解的方法集合进行遍历,并以方法的参数作为key,所有使用了该方法参数的方法集合作为value,保存在subscriptionsByEventType中。

进行了一顿操作,其实核心思想就是对当前注册的类里面的所有的@Subscibe注解的方法,以事件对象(也就是参数)作为key,对代码中所有的找到的类里面使用该事件对象的@Subscibe注解方法进行一个统计到一个集合中,方便后续post的时候进行事件的分发。其实就是一个非常典型的订阅-消费模式,也可以说是观察者模式

2-2、unregister(Object subscriber)

public synchronized void unregister(Object subscriber) {
     
    //上面说过,typesBySubscriber其实只是协助判断是否注册过的
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    //如果是已经注册过的
    if (subscribedTypes != null) {
     
        //在register的时候,会以类为key,类中的所有@Subscribe注解方法参数(事件对香港)作为value
        for (Class<?> eventType : subscribedTypes) {
     
            //遍历集合,从subscriptionsByEventType中移除保存的数据
            unsubscribeByEventType(subscriber, eventType);
        }
        //从map中,将该类对象数据key、value移除
        typesBySubscriber.remove(subscriber);
    } else {
     
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
  • unsubscribeByEventType(Object subscriber, Class eventType)
    这里的逻辑比较简单,有了上面register方法中的分析,相信这个很容易就看懂了,就不多说明了
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
     
    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--;
            }
        }
    }
}

下面对于Event#unregister(Object subscriber)方法做一下简单的总结:

这里用到两个map集合typesBySubscribersubscriptionsByEventType

  • typesBySubscriber 集合实际上就是保存了subscriber下面所有的@Subscribe注解的事件集合,其中key是使用类,value就是事件集合。
  • subscriptionsByEventType是保存的所有的事件对象的方法集合,事件和方法是一对多关系

unRegister操作就是将register方法中找到的类和事件集合关系事件和事件对象方法关系,进行一个清理,防止对象引用导致内存泄漏

因为篇幅原因,post、粘性消息等内容,将拆分到另一篇文章中

你可能感兴趣的:(Android源码解析,android,java,apache,EventBus)