EventBus3.0源码解析——03. register 注册

Posted 夜辉疾风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EventBus3.0源码解析——03. register 注册相关的知识,希望对你有一定的参考价值。

register(注册)

//注册事件接收
EventBus.getDefault().register(this);
//注册事件总线,在获取到eventbus单例后就可以注册了
//注册方法中有两个至关重要的方法:
//1. 寻找到当前object对象中所有带@Subscribe注解标记的方法并保存起来
//2. 对当前object对象中所有带@Subscribe注解的方法进行订阅
public void register(Object subscriber) 
    //获得当前对象的class对象
	Class<?> subscriberClass = subscriber.getClass();
    //通过subscriberMethodFinder对象查找到当前class对象下的所有方法,并存入集合
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//1
    //程序锁
    synchronized (this) 
        //将当前对象下的所有方法遍历
        for (SubscriberMethod subscriberMethod : subscriberMethods) 
            //订阅(对象,方法)
            subscribe(subscriber, subscriberMethod);//2
        
    

findSubscriberMethods(Class)

//缓存当前class对象和对应的所有方法的并发map
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
//查找对象下的所有方法的源码
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) 
    //这里是一个缓存,将当前class对象缓存起来
	List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
	//如果缓存中有,则直接使用缓存的数据
    if (subscriberMethods != null) 
	    return subscriberMethods;
	//是否忽略生成的索引
	if (ignoreGeneratedIndex) 
        //忽略,使用反射的方式查找subscriberMethods
	    subscriberMethods = findUsingReflection(subscriberClass);//1
	 else 
        //不忽略,使用信息的方式查找subscriberMethods
	    subscriberMethods = findUsingInfo(subscriberClass);//2
	
    //如果当前缓存依然为努力了,则报错:当前传递进来的clas是对象没有附带@Subscribe注解对象
	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;
	

findUsingReflection(class)

通过反射的方式查找subscriberMethods

FindState

static class FindState 
    //当前eventbus中记录的所有订阅者方法
    final List<SubscriberMethod>subscriberMethods = new ArrayList<>();
    //将同一个class的方法存放在同一个class中
    final Map<Class, Object> anyMethodByEventType = new HashMap<>();
    //订阅者的方法key集合
    final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    final StringBuilder methodKeyBuilder = new StringBuilder(128);
	//订阅者的class
    Class<?> subscriberClass;
    //当前传递进来的class
    Class<?> clazz;
    //是否跳过父类
    boolean skipSuperClasses;
    //订阅者信息
    SubscriberInfo subscriberInfo;
	
    //初始化
    void initForSubscriber(Class<?> subscriberClass) 
        //订阅class和当前class为同一个
        this.subscriberClass = clazz = subscriberClass;
        //默认不跳过父类
        skipSuperClasses = false;
        subscriberInfo = null;
    
	//清除
    void recycle() 
        subscriberMethods.clear();
        anyMethodByEventType.clear();
        subscriberClassByMethodKey.clear();
        methodKeyBuilder.setLength(0);
        subscriberClass = null;
        clazz = null;
        skipSuperClasses = false;
        subscriberInfo = null;
    
	//检查当前方法的事件类型
    boolean checkAdd(Method method, Class<?> eventType) 
        // 2级检查:只有事件类型的第一级(快速),如果需要,第二级有完整的签名。
        //这里判断当前订阅服务器是否已经监听了相同的方法参数了
        Object existing = anyMethodByEventType.put(eventType, method);
        //没监听,则返回true
        if (existing == null) 
            return true;
         
        //已经监听过了该方法,则判断上一个保存的existing是否是Method
        else 
            //如果existing对象是method类型,则订阅服务器则进行二次检查
            if (existing instanceof Method) 
                //如果eventtype保存的上一个value依然不是当前方法和参数,则抛出异常
                if (!checkAddWithMethodSignature((Method) existing, eventType)) 
                    // 抛出异常:非法状态
                    throw new IllegalStateException();
                
                //官方解释:将任何非方法对象“消费”现有方法
                //这一步的目的暂不清楚,走到这一步,则会返回true
                anyMethodByEventType.put(eventType, this);
            
            //
            return checkAddWithMethodSignature(method, eventType);
        
    
	//检查添加使用的方法签名:1,方法;2,参数的class对象
    //该方法目的是判断当前方法和参数是否已经被存储起来了
    //如果当前的key已经被某个class占用,则返回false
    //如果当前的key没有被占用,或者key存储的class与传递进来的方法的依附class相同,则返回true
    private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) 
        methodKeyBuilder.setLength(0);
        methodKeyBuilder.append(method.getName());//方法名
        methodKeyBuilder.append('>').append(eventType.getName());//参数名
		//生成一个方法key,key = 方法名>参数名
        String methodKey = methodKeyBuilder.toString();
        //当前方法的类class对象
        Class<?> methodClass = method.getDeclaringClass();
        //将当前class对象存储到对应的key之下
        Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
        //如果当前key没有被存储或者当前方法的类class对象是否是其父类
        //有可能是为了防止在找父类时覆盖了子类的方法,因为此方法是子类是重写,方法名参数名完全一样(方法签名);
        //另一个原因是可能是当一个类有多个方法监听同一个event(尽管一般不会这样做),也能将这些方法加进去。
        if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) 
            return true;
         else 
            //如果当前的methodKey已经有值,则这里返回的methodClassOld是上一个存储的值
            //这里还原put,并且返回当前false,当前eventType参数已经保存过了
            subscriberClassByMethodKey.put(methodKey, methodClassOld);
            return false;
        
    
	//清除父类class
    void moveToSuperclass() 
        //如果清除父类class状态为true,则清空clazz
        if (skipSuperClasses) 
            clazz = null;
         else 
            //获得当前clazz的父类class
            clazz = clazz.getSuperclass();
            //获得当前clazz的父类class的名字
            String clazzName = clazz.getName();
            //直接清除当前类的系统级父类,这会提高性能,所有java和javax或者android.开头的都直接清空
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) 
                clazz = null;
            
        
    

//通过反射的方式查找subscriberMethods
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) 
    //FindState状态初始化
	FindState findState = prepareFindState();//1
    //寻找状态初始化订阅者
	findState.initForSubscriber(subscriberClass);
    //如果findState中的class对象不为null,则一直继续循环
	while (findState.clazz != null) 
        //使用反射的方式查找subscriberMethods
	    findUsingReflectionInSingleClass(findState);//3
        //移除父类class
	    findState.moveToSuperclass();
	
    //这个方法非常重要,一个是获取methods,另一个是释放findState里面的map信息
	return getMethodsAndRelease(findState);//2

prepareFindState()

//真个EventBus中只存放4个FinsState对象
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
//初始化FindState
private FindState prepareFindState() 
    //同步锁,缓存当前class对象的并发map
    synchronized (FIND_STATE_POOL) 
        //循环出所有的FindState对象
        for (int i = 0; i < POOL_SIZE; i++) 
            FindState state = FIND_STATE_POOL[i];
            if (state != null) 
                //先将map中的对象置空,这里置空为的是防止并发造成的互相干扰
                FIND_STATE_POOL[i] = null;
                //返回已保存的FindState对象
                return state;
            
        
    
    //map中没有对象则直接new一个FindState对象
    return new FindState();

getMethodsAndRelease(FindState)

//这个方法非常重要,一是获取methods,二是释放findState里面的map信息
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) 
    //将所有方法放入集合中
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    //清空findstate中存储的信息,上一步已经将findstate中subscriberMethods放入了集合中,所以这里清空没有问题
    findState.recycle();//1
    //同步锁
    synchronized (FIND_STATE_POOL) 
        //这里的循环和之前的prepareFindState()方法相呼应
        //简单来说即使一个复用池,如果当前FindState的数组中有空位置则存入FinsState对象
        for (int i = 0; i < POOL_SIZE; i++) 
            //这里将findstate对象赋值,并发的时候这个复用池用的时候隔离,不用了回收
            if (FIND_STATE_POOL[i] == null) 
                FIND_STATE_POOL[i] = findState;
                break;
            
        
    
    return subscriberMethods;

findUsingReflectionInSingleClass(FindState)

//使用反射的方式查找subscriberMethods
private void findUsingReflectionInSingleClass(FindState findState) 
    Method[] methods;//所有方法集合
    try 
        //这比getMethods更快,特别是当订阅者是像activity这样的臃肿类时
        //查找到所有不包含父类的public方法,有助于增加效率
        methods = findState.clazz.getDeclaredMethods();
     catch (Throwable th) 
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        //如果报错这里再使用getMethods()
        methods = findState.clazz.getMethods();
        //跳过了父类方法
        findState.skipSuperClasses = true;
    
    //循环所有方法
    for (Method method : methods) 
        //获得方法的修饰符
        int modifiers = method.getModifiers();
        //方法是public的且没有包含特定的修饰符
        //int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) 
            //获得方法的所有参数
            Class<?>[] parameterTypes = method.getParameterTypes();
            //如果参数只有一个,订阅者只能写一个形参的方法
            if (parameterTypes.length == 1) 
                //获得@Subscribe注解,详见@Subscribe标签介绍
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                //如果有该注解则继续往下走
                if (subscribeAnnotation != null) 
                    //获得当前方法的参数
                    Class<?> eventType = parameterTypes[0];
                    //检查当前方法和参数,如果没有被添加到eventbus中,则往下走
                    if (findState.checkAdd(method, eventType)) 
                        //获得当前订阅者要求的线程类型
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        //subscriberMethods中新添加一个SubscriberMethod(方法,参数,线程类型,优先级,是否分发粘性事件)
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    
                
            
            //如果strictMethodVerification = true且当前方法有@Subscribe注解且参数不为1个,则抛出异常
            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);
            
        
        //如果strictMethodVerification = true;且当前方法带有注解@Subscribe则这里抛出异常
        else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) 
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            //抛出异常:非法的@Subscribe方法:必须是公共的、非静态的和非抽象的方法
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        
    

findUsingInfo(class)

使用方法信息查找subscriberMethods

//用于由注释处理创建的生成索引类的基类
public interface SubscriberInfo 
    Class<?> getSubscriberClass();

    SubscriberMethod[] getSubscriberMethods();

    SubscriberInfo getSuperSubscriberInfo();

    boolean shouldCheckSuperclass();

//订阅者信息索引
//subscriberInfoIndexes是该对象的一个有序集合arraylist
public interface SubscriberInfoIndex 
    SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);

//使用方法信息查找subscriberMethods
//EventBus 3.0刚加的功能,特点是比反射快
//在编译期完成subscriber的注册register,而不是在注册期间
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) 
    //初始化
	FindState findState = prepareFindState();
	findState.initForSubscriber(subscriberClass);
	while (findState.clazz != null) 
        //获得订阅者的信息
	    findState.subscriberInfo = getSubscriberInfo(findState);//1
	    if (findState.subscriberInfo != null) 
            //获得订阅者订阅的所有方法,这一步很关键,如果没有存储某些方法,需要存储到subscriberMethods中
            //获得当前订阅者信息里存储的所有订阅者方法
	        SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
	        for (SubscriberMethod subscriberMethod : array) 
                //依然检查当前eventbus是否存储了该方法和参数
	            if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) 
                    //没有存储则存储到subscriberMethods中
	                findState.subscriberMethods.add(subscriberMethod);
	            
	        
	     else 
            //如果没有获取到订阅者信息,则依然使用反射的方法去获得findState
	        findUsingReflectionInSingleClass(findState);
	    
        //清除当前类的父类
	    findState.moveToSuperclass();
	
    //返回所有当前类记录的需要订阅的方法并且清除Findstatus对象
	return getMethodsAndRelease(findState);

getSubscriberInfo(FindState)

//获得订阅者的信息
private SubscriberInfo getSubscriberInfo(FindState findState) 
    //如果订阅者信息不为null并且订阅者的父类的信息也不为null
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) 
        //将订阅者父类信息赋值给订阅者
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        //如果clazz和订阅者class一致,则返回订阅者信息
        if (findState.clazz == superclassInfo.getSubscriberClass()) 
            return superclassInfo;
        
    
    //如果有订阅者信息索引则直接从索引中拿出来
    if (subscriberInfoIndexes != null) 
        for (SubscriberInfoIndex index : subscriberInfoIndexes) 
            //从索引中获取当前findstate的class对象所存储的订阅者信息
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) 
                return info;
            
        
    
    return null;

这里有一个延申,订阅者信息索引的保存方法,EventBusBuilder中的addIndex(SubscriberInfoIndex)

注:源码中addIndex这个方法并没有用到,暂不清楚订阅者信息索引如何起作用的

subscribe(Object,SubscriberMethod)

当执行register的时候,会将当前注册的对象中的所有带有@Subscribe注解标记的方法都收集起来,上面这部

以上是关于EventBus3.0源码解析——03. register 注册的主要内容,如果未能解决你的问题,请参考以下文章

EventBus3.0源码解析——06. 总结

EventBus3.0源码解析——05. unregister 注销

Android EventBus3.0使用及源码解析

EventBus3.0源码解读

EventBus 3 源码解析

EventBus 3 源码解析