美文网首页
Android EventBus源码解析(上)

Android EventBus源码解析(上)

作者: Android程序员老鸦 | 来源:发表于2021-05-26 20:18 被阅读0次
    EventBus作为优秀的事件订阅响应开源库,用着确实很爽,不由得想要了解一下它的实现原理,看看它的源码,结合网上其他大神的分享成果,自己也是硬生生啃了一遍源码,看完觉得收获很大,决定把这个过程记录一下,方便以后参考。

    因为EventBus里有用到APT相关技术,所以对APT不了解的小伙伴可以先看看我另外一篇文章Android 利用apt生成代码,实现butterKnife控件查找功能
    废话不多说,首先看看EventBus的一般用法:

    public class TestRunnerActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_runtests);
            //第一步,注册
            EventBus.getDefault().register(this); 
        }
        //第二步,订阅事件
        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onEventMainThread(TestFinishedEvent event) {
             Log.d("eventBus","收到事件");
        }
        @Override
         public void onDestroy() {
            //最后不要忘了解除注册
            EventBus.getDefault().unregister(this);
            super.onDestroy();
        }
    }
        
    

    以上就是EventBus的订阅事件的步骤,首先从注册方法register()开始看:

       public void register(Object subscriber) {
            Class<?> subscriberClass = subscriber.getClass();
            //关注点1:通过类名也就是activity的类查找SubscriberMethod集合
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    //关注点2:从方法名来看是订阅动作
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    

    先看关注点1,这个SubscriberMethod类是个什么东东呢,看看它的源码:

    public class SubscriberMethod {
        final Method method; //方法
        final ThreadMode threadMode;//线程类型
        final Class<?> eventType;//事件类型
        final int priority; //优先级
        final boolean sticky; //是否粘性事件
    
        public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
            this.method = method;
            this.threadMode = threadMode;
            this.eventType = eventType;
            this.priority = priority;
            this.sticky = sticky;
        }
        //....省略干扰代码
    }
    

    这里列出SubscriberMethod的主要成员变量和构造函数,这些成员变量一看就非常熟悉,不就是我们在activity里写的用来接收event事件的方法的一个封装类嘛?
    回到关注点1,继续看看findSubscriberMethods(subscriberClass)这个函数:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
            //先从缓存取,放在以类名为key的map里
            List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
            if (subscriberMethods != null) {
                return subscriberMethods;
            }
            //ignoreGeneratedIndex 忽略注解生成器生成的MyEventBusIndex,这个类包含了app里所有注解了subscriber
            //的方法的信息,可以快速获取快速响应而不必在运行时用反射去实时获取,默认是false
            if (ignoreGeneratedIndex) {
                //通过反射来查找订阅类信息
                subscriberMethods = findUsingReflection(subscriberClass);
            } else {
               //注解生成器MyEventBusIndex类中获得订阅类的订阅方法信息,用SubscriberMethod类封装
                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;
            }
        }
    

    因为我们在使用EventBus的过程中是使用getDefault()的方法来实例化EventBus,所以ignoreGeneratedIndex就是默认的false,继续看findUsingInfo(subscriberClass)方法:

     private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
            //创建一个FindState对象,这个类的作用是在查找所有订阅信息的过程中的一个暂时存放订阅数据的封装类,主要是存放List<SubscriberMethod>,prepareFindState()方法里用到了享元模式,暂不深究
            FindState findState = prepareFindState();
            //初始化一下,每次不同的activity/Fragement进来的时候需要初始化一下订阅类的类型
            findState.initForSubscriber(subscriberClass);
            //这个循环一般只会走一遍,这里涉及到向父类查找订阅事件的问题,我们一般就在当前activity订阅事件
            while (findState.clazz != null) {
                //getSubscriberInfo()这个方法返回的findState.subscriberInfo必然是空,原因还是我们初始化EventBus的时候用的getDefault()获取实例,没有添加EventBus apt生成的MyEventBusIndex类的实例,从而导致实际运行代码的时候还是会走反射的路线,降低app运行性能,高性能的做法后面会讲到
                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 {
                    //没有手动添加addIndex(MyEventBusIndex())还是会走反射流程,这个方法在之前的findUsingReflection()也有调用到
                    findUsingReflectionInSingleClass(findState);
                }
                findState.moveToSuperclass();
            }
           //通过findUsingReflectionInSingleClass(findState)方法拿到了订阅方法集合放在findState,getMethodsAndRelease会提取这个集合返回回去
            return getMethodsAndRelease(findState);
        }
     //FindState类
      static class FindState {
            //SubscriberMethod的集合
            final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
            final Map<Class, Object> anyMethodByEventType = new HashMap<>();
            final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
            final StringBuilder methodKeyBuilder = new StringBuilder(128);
            //activity类名
            Class<?> subscriberClass;
            Class<?> clazz;
            boolean skipSuperClasses;
            //
            SubscriberInfo subscriberInfo;
    
            void initForSubscriber(Class<?> subscriberClass) {
                this.subscriberClass = clazz = subscriberClass;
                skipSuperClasses = false;
                subscriberInfo = null;//置为null了
            }
          //省略干扰代码。。。
    }
    //试图通过subscriberInfoIndexes (指的就是apt生成的辅助类)获取订阅信息,但subscriberInfoIndexes 我们没有指定,所以最终返回null
     private SubscriberInfo getSubscriberInfo(FindState findState) {
            //findState.subscriberInfo为null,findState初始化的时候为null了
            if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
                SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
                if (findState.clazz == superclassInfo.getSubscriberClass()) {
                    return superclassInfo;
                }
            }
            //subscriberInfoIndexes 为null,除非在构造EventBus的时候addIndex(MyEventBusIndex())
            if (subscriberInfoIndexes != null) {
                for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                    SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                    if (info != null) {
                        return info;
                    }
                }
            }
            return null;
        }
    
    

    继续看findUsingReflectionInSingleClass(findState)方法做了啥,看方法名,就是用反射去查找订阅信息:

    private void findUsingReflectionInSingleClass(FindState findState) {
            Method[] methods;
            try {
                //用到反射,拿到类里所有的方法
                methods = findState.clazz.getDeclaredMethods();
            } catch (Throwable th) {
                 //省略干扰代码。。。
            }
            for (Method method : methods) {
                int modifiers = method.getModifiers();
                //判断方法的格式,必须用public修饰,不是抽象的,参数只能是1个
                if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                    //获取方法的参数数组
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length == 1) {
                        //走到这个条件才是满足的方法,其他的都会报错,用多了EventBus应该对那些报错信息有印象
                        //获取方法的Subscribe 注解
                        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                        if (subscribeAnnotation != null) {
          
                            Class<?> eventType = parameterTypes[0];
                            if (findState.checkAdd(method, eventType)) {
                                //解析Subscribe 注解的各个值,线程模式,优先级这些
                                ThreadMode threadMode = subscribeAnnotation.threadMode();
                                //最终封装成SubscriberMethod放在findState对象的subscriberMethods集合中
                                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");
                }
            }
        }
    

    至此总算拿到了所有的activity/fragment里的注解了Subscribe的方法集合List<SubscriberMethod>,接下来看看之前一直提到的EventBus Apt生成的代码文件MyEventBusIndex,这里贴出EventBus官方源码生成的MyEventBusIndex文件:

    /** This class is generated by EventBus, do not edit. */
    public class MyEventBusIndex implements SubscriberInfoIndex {
        //注意这个map,里面存储的是以订阅类名为key,订阅信息为值的一个键值对,就是在这里存储了app工程里所有的订阅信息,
        private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
        //静态代码块,在编译期间就找好了所有的订阅类信息,putIndex方法就是把所有的注册过EventBusd 类和订阅信息搜集起来,从而不必在运行时去做这个事情
        static {
          
            SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusBackground.class,
                    true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onEventBackgroundThread", TestEvent.class, ThreadMode.BACKGROUND),
            }));
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscriberClassEventBusAsync.class,
                    true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onEventAsync", TestEvent.class, ThreadMode.ASYNC),
            }));
    
            putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onEventMainThread", TestFinishedEvent.class, ThreadMode.MAIN),
            }));
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusMain.class,
                    true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onEventMainThread", TestEvent.class, ThreadMode.MAIN),
            }));
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.SubscribeClassEventBusDefault.class,
                    true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onEvent", TestEvent.class),
            }));
    
            putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscribeClassEventBusMainOrdered.class,
                    true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("onEvent", TestEvent.class, ThreadMode.MAIN_ORDERED),
            }));
    
        }
    
        private static void putIndex(SubscriberInfo info) {
            SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
        }
        //这个方法拿到相应类的订阅信息,在上面有getSubscriberInfo(FindState findState)方法里有被调用
        @Override
        public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
            SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
            if (info != null) {
                return info;
            } else {
                return null;
            }
        }
    }
    

    所以用EventBus的高效写法应该是这样初始化EventBus单例:

    EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus()

    并且应该在application里实例化他,这样在activity里就能直接getDefault()获取到单例了,这样才利用起来了EventBus Apt生成的辅助类MyEventBusIndex,从而提高了app性能。
    篇幅有限,下篇继续研究关注点2 subscribe()方法和Post事件流程。
    Android EventBus源码解析(下)

    相关文章

      网友评论

          本文标题:Android EventBus源码解析(上)

          本文链接:https://www.haomeiwen.com/subject/lgxksltx.html