当前位置: 首页 > news >正文

EventBus 3.0源码解析

综述:

  EvenBus已迥然成为了我们项目中常用的第三方开源框架,其使用简单功能强大,而且他的内部原理也十分简单,那么就让我们一起来看看他的内部实现吧!

EventBus流程:

   从图片我们可以看出来EventBus的使用真的是十分简单,事件的发布者只需要将事件"post"的到EventBus中,再由EventBus将事件传给订阅者  ( 图片摘抄自EventBus的github)

具体分析:

  使用过Eventbus的人都不会对下面的那句代码陌生

    EventBus.getDefault().register(this);
复制代码

  这句代码便是我们在Subscriber,也就是我们经常使用的Activity或者Fragment的OnCreate的注册Eventbus的代码,那么自然地字段代码就变得很重要,因为我们猜测他一定做了很多的初始工作:

    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
复制代码

  我们看到了在getDefault()方法里面是一个Double Check的单例模式,构造了一个唯一的EventBus对象,那么我们再去看看EventBus的构造函数里面写的是什么

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);//这个参数一般为null false false 
        logSubscriberExceptions = builder.logSubscriberExceptions; //一般情况为true
        logNoSubscriberMessages = builder.logNoSubscriberMessages;//一般情况为true
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;//一般情况为true 
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;//一般情况为true  
        throwSubscriberException = builder.throwSubscriberException;//一般情况为false 
        eventInheritance = builder.eventInheritance;//一般情况为true
        executorService = builder.executorService;//一个newCachedThreadPool()  
    }

复制代码

  果不其然,这里做了很多的初始化的事情,用的是EventBusBuilder,一个创建EventBus对象的Builder, 我参照其中的参数值在上面的注释中注释上了一般情况下的默认值。这里还出现了一个SubscriberMethodFinder类,我们来看一下面的三个属性,我们后面要讲到

 private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
 private List<SubscriberInfoIndex> subscriberInfoIndexes;
 private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
复制代码

  初始化都弄完啦,那么我们在先来看register方法

   public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();//获取到传过来的类对象
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//获取类中的订阅方法
        synchronized (this) {//循环所有的订阅方法完成订阅
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
复制代码

  我们看到了register方法里面做了两件重要的事情,第一便是获取类中的订阅方法,第二便是将所有的方法完成订阅。那么我们先来看第一个获取订阅方法,那么他是怎么获取的呢又是什么样子的方法才是订阅方法呢。我们继续往下看subscriberMethodFinder.findSubscriberMethods这个方法里写了什么

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);//先在方法缓存区里面寻找有没有订阅方法
        if (subscriberMethods != null) {
            return subscriberMethods;//如果有就返回这些方法
        }
     //根据ignoreGeneratedIndex来判断使用什么方法来获取订阅方法
        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;
        }
    }
复制代码

  在上面的代码中我们见到了METHOD_CACHE这个没有第一个没有将的Map,他的键是一个类对象,值是一个List,里面存储的是订阅方法。那么就把这个称之为方法缓存区。还有呢就是findUsingReflection和findUsingInfo,这两个都是获取订阅方法,他们有什么差异呢,为什么ignoreGeneratedIndex的默认值是false呢?那我们带着疑惑来看这个两个方法的代码吧。

  private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }
复制代码

  通过方法的名字大家也猜测到了这个方法里面使用的是反射来获取subscriberClass中的订阅方法的。那么这里面还有一个prepareFindState和initForSubscriber方法。我们继续来看他们的代码。

   private FindState prepareFindState() {
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {//初始化设置成null
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
    }
复制代码

  这个方法就是得到了一个来装订阅方法的一个FindState类,初始设置成null。

    void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }
复制代码

  initForSubscriber方法将FindState中的subscriberClass赋值,并将subscriberInfo设置为null

this.subscriberClass = clazz = subscriberClass
复制代码

  那么剩下的便是反射获取订阅方法最主要的方法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
            methods = findState.clazz.getMethods();得到类中所有的方法
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {//进行方法的循环
            int modifiers = method.getModifiers();//得到方法的修饰 类似public private等
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            //如果方法不是public的并且不是abstract和static的
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {//如果方法只有一个参数
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                    //如果方法的注释是@Subscribe
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                           //获取这个方法的订阅线程 通过(threadMode = ThreadMode.MAIN)来设置
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            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)) {
            //否则报类不是public 或者类是静态抽象的错误
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }
复制代码

  通过上面的代码注释我们就又明白了什么叫做订阅方法(虽然你们早就知道啦),便是下面代码中呈现的方法

 @Subscribe(threadMode = ThreadMode.MAIN,priority = 1,sticky = true)
 public void onMessageEvent(MessageEvent event) {/* Do something */};
复制代码

  我们接下来看看findUsingInfo方法,其实这个方法是通过apt(Annotation Processing Tool )方法来得到所有的订阅方法的。

  private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);//看这里 通过apt获取订阅方法
            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;
            }
        }
        if (subscriberInfoIndexes != null) {//subscriberInfoIndexes 是通过apt处理器自动生成的文件
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }
复制代码

  通过这一系列,我们便得到了订阅类中的订阅方法,但是这还远远不过,因为我们还没有subscribe的,这也就是我们一开始留下的坑,现在来填上吧!

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;//获取订阅方法中的参数类型
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//构建一个订阅事件类
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//先根据这个参数类型来获取Subscription
        if (subscriptions == null) {//如果是空的话说明没有注册过这个参数类型的Subscription
            subscriptions = new CopyOnWriteArrayList<>();
            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;
            }
        }
         //获取typesBySubscriber中的订阅事件
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {//如果事件是null的话 就添加到typesBySubscriber中
            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<Class>).
                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);
            }
        }
    }

复制代码

  最后让我们来看事件发送的post方法,这里面主要是根据线程来见里面所有的event全都发送给订阅者。

 public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();//获取当前线程
        List<Object> eventQueue = postingState.eventQueue;//获取当前线程的eventQueue
        eventQueue.add(event);//将事件发送到queue中

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {//如果eventQueue不为empty 就将其中的event一个一个的发送出去
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

复制代码

  以上呢就是我对eventbus的源码分析,还有好多地方没有分析到,只分析了订阅事件是如何创建,添加到eventQueue中,又如何将eventQueue中的订阅消息发送给订阅者。就是抛砖引玉,让大家有了一个笼统的概念可以继续看eventbus的代码,如有分析的不恰当的地方还请大家指出来。

转载于:https://juejin.im/post/5ad6ab19518825557b4d4360

相关文章:

  • SHELL传参简单实例
  • 如何选择开源的机器学习框架?
  • python shutil
  • Git 使用笔记
  • 数值实验-高斯消元LU分解
  • JSONP的跨域原理
  • Spring Boot 部署与服务配置
  • FormData的使用及input文件上传
  • 工业强基 - 头条新闻
  • CPU和内存 程序(线程)关系
  • 双循环递归匹配路由表
  • editplus 注册码
  • 在窗体中把DataGridView中的数据导出Excel
  • Linux学习笔记4月19日任务
  • 《Linux学习并不难》用户管理(1):Linux用户账户分类
  • python3.6+scrapy+mysql 爬虫实战
  • @angular/forms 源码解析之双向绑定
  • gcc介绍及安装
  • Git学习与使用心得(1)—— 初始化
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • Vue.js源码(2):初探List Rendering
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 简析gRPC client 连接管理
  • 解析带emoji和链接的聊天系统消息
  • 入手阿里云新服务器的部署NODE
  • 提醒我喝水chrome插件开发指南
  • Prometheus VS InfluxDB
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (4)Elastix图像配准:3D图像
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (图)IntelliTrace Tools 跟踪云端程序
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (转)【Hibernate总结系列】使用举例
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
  • .Net转前端开发-启航篇,如何定制博客园主题
  • @Autowired自动装配
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • [23] GaussianAvatars: Photorealistic Head Avatars with Rigged 3D Gaussians
  • [Android]使用Android打包Unity工程
  • [BZOJ2850]巧克力王国
  • [Docker]十二.Docker consul集群搭建、微服务部署,Consul集群+Swarm集群部署微服务实战
  • [element-ui] el-dialog 中的内容没有预先加载,因此无法获得内部元素的ref 的解决方案
  • [HDOJ4911]Inversion
  • [INSTALL_FAILED_TEST_ONLY],Android开发出现应用未安装
  • [Kubernetes]9. K8s ingress讲解借助ingress配置http,https访问k8s集群应用
  • [Linux]进程间通信(system V共享内存 | system V信号量)
  • [Matlab有限元分析] 2.杆单元有限元分析
  • [NOIP2014] 提高组 洛谷P1941 飞扬的小鸟
  • [Python] 集合操作及方法总结
  • [Python]装饰器