首页 > 编程语言 >EventBus源码再分析

EventBus源码再分析

时间:2023-08-21 18:22:09浏览次数:30  
标签:分析 subscriptions subscriber Subscribe 源码 postingState EventBus event subscriptio

一、概述

  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)方法中都干了啥

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
     //先判断之前装载了没有,如果装载了就直接拿来使用,不用继续往下执行,如果没有装载则往下执行 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);//拿到注解方法集合

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) {
            //获取方法上的@Subscribe注解 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) {//如果Subscribe对象不为空,说明这个方法上有@Subscribe注解 Class<?> eventType = parameterTypes[0];//拿到第一个参数,用于确定事件类型 if (findState.checkAdd(method, eventType)) {
                  //获取与这个注解相关联的线程模型 ThreadMode threadMode = subscribeAnnotation.threadMode();
                  //将这个注解的信息存入集合,包括:方法对象、事件类型、线程模型等 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);方法

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
    //将注册对象和注册对象中的@Subscribe注解信息封装成为一个Subscription,方便后面使用 Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //先看看以前有没有存过,如果没有就new一个 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>();
       //把事件类型和时间类型对应的对象及对象注解信息存入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) {
          //把封装好的注册对象和对象包含的注解方法信息存入集合 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)方法

public void post(Object event) {
     //事件入队列 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()) {
            //分发事件 postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }

  下面看看postSingleEvent都干了什么事情

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方法中时什么东西

void invokeSubscriber(Subscription subscription, Object event) {
        try {
       //从对象中拿到注解方法对象,然后调用方法对象的invoke,把参数传递进去主动执行 subscription.subscriberMethod.method.invoke(subscription.subscriber, event); }
      .... }

  到这里post方法算是执行完事了。其实Post方法说是发送事件倒不如说是,主动执行事件。我们可以看下它所做的操作:1.根据事件类型,拿到注解方法集合、2.遍历集合、3.遍历后拿到@Subscribe注解标注的方法对象,然后执行method.invoke方法。这样看起来就是就是主动执行。

  三、最后看看EventBus.getDefault().unregister(obj)干了啥

public synchronized void unregister(Object subscriber) {
    //根据对象拿到类型集合,这是register的时候存好的 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { for (Class<?> eventType : subscribedTypes) {
          //循环遍历取消注册 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--; } } } }

 

标签:分析,subscriptions,subscriber,Subscribe,源码,postingState,EventBus,event,subscriptio
From: https://www.cnblogs.com/tony-yang-flutter/p/17646749.html

相关文章

  • ASP.NET版LIMS系统源码 实验室信息管理系统
    实验室信息管理系统(LaboratoryInformationManagementSystem)简称LIMS系统,是指通过计算机对实验室的各种信息进行管理的计算机软、硬件系统,并将实验室的设备各种信息通过计算机网络连接起来,采用科学的管理思想和先进的数据库技术,实现以实验室为核心,集检验业务管理、检测资源管理、......
  • RocketMQ源码(四):RocketMQ生产者发送消息流程
    RocketMQ通过Producer发送消息,以同步方式发送普通消息为例,分析发送消息的整体流程。Producer的示例代码如下:1importorg.apache.rocketmq.client.producer.DefaultMQProducer;2importorg.apache.rocketmq.client.producer.SendResult;3importorg.apache.rocketmq.......
  • kube-scheduler 启动分析
    先看一段kubernetesscheduler的描述:TheKubernetesschedulerisacontrolplaneprocesswhichassignsPodstoNodes.TheschedulerdetermineswhichNodesarevalidplacementsforeachPodintheschedulingqueueaccordingtoconstraintsandavailableresou......
  • 时序数据高基数问题分析与解决方法
    01WhatisHigh-Cardinality基数(Cardinality)在数学中定义是用来代表集合元素个数的标量,比如对于有限集合A={a,b,c}的基数就是3,对于无限集合也有一个基数概念,但是今天主要谈论的是计算机领域,就不在这里展开。在数据库的上下文里面,基数并没有严格的定义,但大家对基数的共识......
  • (一)Dubbo源码解析:增强SPI
    〇、前言在Dubbo的架构设计中,如何可以通过“类插拔”的方式,对其功能进行灵活的扩展或者削弱,那么,SPI起到了极其关键的作用。本篇文章作为分析Dubbo源码的第一篇文章,我们先暂时放下“服务注册发布流程”、“服务启动流程”、“请求处理流程”……这些功能代码的探索,我们先从最基本的......
  • 【校招VIP】产品分析能力之用户画像出发
    考点介绍:用户行为和交互是产品经理能力的重要部分,在校招中,基于用户画像的分析题和设计题也是高频考点。一、考点题目1.爱奇艺中搜索关键词“音乐直播”,分析这个关键词可能的具体用户需求。解答:关于搜索关键词“音乐直播”的场景有以下几种可能......2.如果让你利用网络推广成......
  • 逻辑清晰,详解社交源码Android开发SDK
    前篇我们讲解了有关如何在IOS平台开发集成SDK,那么今天来给大家简单讲解下如何在社交源码Android客户端上开发集成。1.获取SDK:从提供SDK的第三方开发者或公司获得SDK的相关文件和文档。2.导入SDK文件:将SDK的库文件(.jar或.aar格式)拷贝到Android项目的libs文件夹中。3.配置权限:检查并......
  • 29、telnet远程访问的安全分析
    在telent远程访问协议中,可以通过wireshark抓取报文,就会看到用户名和密码,说明在网络中传输有风险。而使用ssh远程访问协议,抓取报文时就会加密显示。所以平时使用ssh远程访问网络设备安全性更可靠。1、搭建虚拟环境测试拓扑如下: 2、路由器配置AR1配置如下:<Huawei>Aug21202......
  • app直播源码,读取多行文本、读取文件分割多行文本
    app直播源码,读取多行文本、读取文件分割多行文本读取文本 publicfunctiondaoru(){/* *逐行读取TXT文件  */     $rep=str_replace("\n",',',"TD92069E76EC27CA8B66B631CB49A9C6TD5A22D898050393C2F8D5C29C854F1B");    $cont=explode(',',$re......
  • 直播系统源码,实现上滑加载分页(触底加载)
    直播系统源码,实现上滑加载分页(触底加载) //依据分类查询图书  publicfunctionquery_book_by_classid(){    $token=input('token');    $class_id=input('class_id');    $page=input('page');//起始行    $per_page=input('per_page');//......