Android框架源码解析之(五)Retrofit

2023-11-17

源码地址: https://github.com/square/retrofit

Retrofit源码结构:
在这里插入图片描述
在这里插入图片描述

可以看出Retrofit是使用idea,maven依赖编写的Java工程,并不是一个Android 工程。

Retrofit的简单使用:
1、创建Service接口

public interface HistoryDataService {
    @GET("japi/toh?v=1.0&month=10&day=1&key=956c0df840307c89f68271325ccc8454")
    Call<HistoryToday> getResult();
}

2、创建 Call

        retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())//gson自动解析
                .baseUrl(NetworkConstant.JUHE_URL)//主机地址
                .build();
                
        HistoryDataService historyDataService = retrofit.create(HistoryDataService.class);
        Call<HistoryToday> call = historyDataService.getResult();

3、使用Call执行网络请求

        call.enqueue(new Callback<HistoryToday>() {
            @Override
            public void onResponse(Call<HistoryToday> call, Response<HistoryToday> response) {
                
            }

            @Override
            public void onFailure(Call<HistoryToday> call, Throwable t) {

            }
        });

Retrofit源码解析:
1、Retrofit 的创建过程
Retrofit是通过建造者模式构建出来的,看一些细节

//Retrofit.java
    public Builder() {
      this(Platform.get());
    }

看一下Platform的get方法

//Platform .java
class Platform {
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {//根据不同的平台获取不同的线程池
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("org.robovm.apple.foundation.NSObject");
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
  ......
  }

Platform 的get 方法最终调用了findPlatform方法,根据不同的平台获取不同的线程池。
看一下Android 的线程池:

  static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);//使用Handler将 Runnable 转发到UI线程
      }
    }
  }

其实就是个回调接口,使用Handler将 Runnable 转发到UI线程。

查看Build方法:

public Retrofit build() {
      if (baseUrl == null) { //baseurl不能为空,否则抛异常
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) { //如果没有手动设置callFactory,则直接new OkHttpClient, 用于后续网络请求
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) { //如果没有手动设置callbackExecutor,则获取平台默认的callbackExecutor,用于将Runable转发到UI线程,AndroidPlatform 默认使用Handler.post(Runable)
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      // 添加calladapter 用于后期子线程到主线程的回调
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // Make a defensive copy of the converters. //存储转化对象,例子中调用addConverterFactory(GsonConverterFactory.create()) ,设置返回的数据转化为Gosn可以解析的对象
      List<Converter.Factory> converterFactories = new ArrayList<>( //converterFactories可以设置多个,retrofit会自动选择使用哪个converterFactoriy 解析数据
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  }

2、Call 的创建过程

HistoryDataService historyDataService = retrofit.create(HistoryDataService.class);
  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }  //一些有效性验证
                              //动态代理
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              private final Platform platform = Platform.get();
                                         //proxy 被代理的对象,method 被代理的方法,args方法参数
              @Override public Object invoke(Object proxy, Method method, Object... args)
                      throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);//如果是method是object的,则可以直接调用
                }
                if (platform.isDefaultMethod(method)) {//java8中接口引入默认方法,如果是接口的默认方法,直接调用
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                ServiceMethod serviceMethod = loadServiceMethod(method); //这个method是 HistoryDataService 的getResult()方法
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);  //这里创建ExeExecutorCallbackCall
              }
            });
  }

loadServiceMethod方法:

private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();

  ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {  //主要起一个缓存作用,如果存在method对应的ServiceMethod ,直接返回,如果不存在的话,创建并存入
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

查看 ServiceMethod.Builder(this, method).build();

  public ServiceMethod build() {
    callAdapter = createCallAdapter();//获取retrofit创建时callAdapter
    responseType = callAdapter.responseType();//返回数据的真实类型
    if (responseType == Response.class || responseType == okhttp3.Response.class) {//一些校验
      throw methodError("'"
              + Utils.getRawType(responseType).getName()
              + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    responseConverter = createResponseConverter();//从retrofit创建时的responseConverter中,遍历,获取一个合适的Convert用来转换对象

    for (Annotation annotation : methodAnnotations) {//对方法注解进行遍历解析请求方式和请求地址
      parseMethodAnnotation(annotation);
    }
//下面的代码主要对状态进行一系列的判断

    if (httpMethod == null) {
      throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
    }

    if (!hasBody) {
      if (isMultipart) {
        throw methodError(
                "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
      }
      if (isFormEncoded) {
        throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
                + "request body (e.g., @POST).");
      }
    }

    int parameterCount = parameterAnnotationsArray.length;
    parameterHandlers = new ParameterHandler<?>[parameterCount];
    for (int p = 0; p < parameterCount; p++) {
      Type parameterType = parameterTypes[p];
      if (Utils.hasUnresolvableType(parameterType)) {
        throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
                parameterType);
      }

      Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
      if (parameterAnnotations == null) {
        throw parameterError(p, "No Retrofit annotation found.");
      }

      parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
    }

    if (relativeUrl == null && !gotUrl) {
      throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
    }
    if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
      throw methodError("Non-body HTTP method cannot contain @Body.");
    }
    if (isFormEncoded && !gotField) {
      throw methodError("Form-encoded method must contain at least one @Field.");
    }
    if (isMultipart && !gotPart) {
      throw methodError("Multipart method must contain at least one @Part.");
    }

    return new ServiceMethod<>(this);
  }

ExecutorCallbackCall部分代码如下所示:

  static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void enqueue(final Callback<T> callback) {
      if (callback == null) throw new NullPointerException("callback == null");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() { //前面讲过,将请求结果回调到主线程
            @Override public void run() {
              if (delegate.isCanceled()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

其实是将 call 封装为 ExecutorCallbackCall ,前面用到了适配器模式。
通过ExecutorCallbackCall 将请求结果回调到主线程。

3、Call 的 enqueue方法

 @Override public void enqueue(final Callback<T> callback) {
    call.enqueue(new okhttp3.Callback() {//实际上调用的是okhttp3 Call 类型的call 的enqueue方法
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);//解析response
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

 }

查看parseResponse 方法:

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();//根据返回的不同状态码做不同的操作。
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }

    if (code == 204 || code == 205) {
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = serviceMethod.toResponse(catchingBody);// 将 catchingBody 转为body
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

看一下 serviceMethod的toResponse方法:

  /** Builds a method return value from an HTTP response body. */
  T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);//这块将body转换为json,
  }

在之前的例子中,使用的是GsonConvertFactory ,查看一下GsonConvertFactory 的代码

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter); //创建 GsonResponseBodyConverter
  }

查看GsonResponseBodyConverter 源码:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}

在 GsonResponseBodyConverter 的 convert 方法里将回调的数据转为Json格式,。

call的enqueue方法主要做的事是用Okhttp来请求网络,并将返回的Response进行数据转换回调给UI线程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android框架源码解析之(五)Retrofit 的相关文章

  • QT表格控件实例(Table Widget 、Table View)

    欢迎小伙伴的点评 相互学习 博主 本着开源的精神交流Qt开发的经验 将持续更新续章 为社区贡献博主自身的开源精神 文章目录 前言 一 图示实例 二 列表常用成员解析 三 代码实例解析 UI设计如下 mainwindow h main cpp
  • 使用Rest API设计简单的博客网站

    博客根地址 https mygithub com 对于每个用户自己的博客网站都在这个根地址后加url信息 使用Rest API进行设计 在这里设计如下API https mygithub com username articles http
  • python解释器的安装与配置

    目录 1 Python解释器安装配置 2 Python环境变量设置 3 Python解释器多个版本共存 1 Python解释器安装配置 python解释器是能够解释执行 Python代码的程序 它可以解析和执行 Python 程序 首先前往
  • c#之构造函数和析构函数

    如有错误 欢迎指正
  • 【五一创作】某头条参数破解并实现界面化搭建

    某条参数破解并实现界面化搭建 前言 效果展示 难点 参数逆向破解 signature ac signature s v web id 界面化实现 总结 前言 趁着日常闲余时间 想着搞一搞某条的反爬 练练手 想到自己很久没开发过前端界面了 有
  • JAVA线程 -- wait notify notifyAll

    在通常的代码中实现线程互斥用的较多的是synchronized synchronized this 与synchronized static Object 的区别 synchronized就是针对内存区块申请内存锁 this关键字代表类的一
  • Apache+PHP+MySQL环境搭建超详细!!!

    前言 最近在学习PHP语言 整理了一下关于环境搭建的部份 也可以选择集成环境会更方便 自己搭建环境会更好的理解原理 适合初学者 会持续更新哟 确定服务器的VC版本 一定要看 避免后面的错误 版本不一致会导致Apache在加载php包的时候出
  • AcWing 378. 骑士放置(最大独立集&&匈牙利算法)

    输入样例 2 3 0 输出样例 4 解析 题意为求最大独立集 即为总点数 最小点覆盖 include
  • 逆序输出数组元素

    题目描述 从键盘上输入10个整数 存储在一个长度为10的整型数组中 要求将输入的10个数逆序输出 如输入为 0 1 2 3 4 5 6 7 8 9 输出为 9 8 7 6 5 4 3 2 1 0 输入 10个整数 以空格分隔 输出 将输入的
  • Linux定时同步时间、定时任务、crontab

    Linux定时同步时间 定时任务 crontab 1 安装ntp 2 手动同步 3 确定服务器是否开启定时任务计划服务 4 创建 修改 删除定时任务 5 查看定时任务 6 查看定时任务的执行情况 7 定时任务备份恢复 8 停止crontab
  • RAC 网路结构 network

    Reliable Data Socket 协议配置Oracel Clusterware 的通信方式 默认使用的是UDP协议 在windows平台上默认使用TCP IP协议进行通信 用户不能使用内联网络与数据库进行通信 因为Cache Fus
  • 基于C#的数据库文件管理助手

    我们经常会遇到这样的问题 在数据库中的文件存放的是web格式或者是绝对路径 以及使用的是百度上传或者其他上传组件 造成了很多异步上传的冗余文件 如果客户需要我们导出企业官网中的产品图片 我们该如何处理 很简单 当然是自己写个工具来读取 然后
  • obsidian memos插件iphone端无法读取memos

    首先看periodic或者daily插件有没有问题 其次看dateview插件是否达到要求的0 5 9以上 如果是手机端不行但电脑端可以的话一般来说插件配置是没问题的 除非手机app有哪里不兼容 都没有问题的话可能是 memos插件在手机上
  • CMake中cmake_minimum_required的使用

    CMake中的命令cmake minimum required用于设定需要的最低版本的CMake 其格式如下 cmake minimum required VERSION
  • 与窗口大小无关的图像滤波算法

    问题 一幅24位彩色图像 其上有一些红色区域 这些区域都是相连的 为了将图像中红色且相连的部分分离出来 使用如下算法 对单个像素进行判断 若满足R 10 gt G且R 10 gt B 那么就认为该像素呈红色 因为图像原因 可能在某个位置孤立
  • spingboot+layui+poi实现excel导入功能,并批量插入数据库

    1 maven的pom文件添加下面jar包
  • 堆(Heap-线程共享)-运行时数据区

    是被线程共享的一块内存区域 创建的对象和数组都保存在 Java 堆内存中 也是垃圾收集器进行 垃圾收集的最重要的内存区域 由于现代VM采用分代收集算法 因此Java堆从GC的角度还可以 细分为 新生代 Eden 区 From Survivo
  • 2023深圳杯C题

    专栏内已发布ABCD篇 论文 思路 代码 只需订阅一次 目录 无人机协同避障航迹规划 摘要 一 问题重述 1 1 背景

随机推荐

  • 10分钟搞懂遗传算法

    大自然有种神奇的力量 它能够将优良的基因保留下来 从而进化出更加强大 更加适合生存的基因 遗传算法便基于达尔文的进化论 模拟了自然选择 物竞天择 适者生存 通过N代的遗传 变异 交叉 复制 进化出问题的最优解 遗传算法看似神奇 但实现思路却
  • ERROR 1045 (28000): Access denied for user ‘ODBC‘@‘localhost‘ (using password: NO)

    ERROR 1045 28000 Access denied for user ODBC localhost using password NO 这个问题是由于密码错误导致的 一般是要重置密码 可以修改my ini配置文件 1 用记事本打开
  • C语言中数组名和&数组名的区别

    C语言中数组名和 数组名的区别 include
  • 迁移学习源码全注释 - 《Tensorflow 实战 Google 深度学习框架》源码注释

    学习迁移学习源码 做了完全版本注释 以做记录 coding utf 8 Created on Mon Dec 25 12 30 25 2017 需要提前下载训练好的 Inception V3模型 以及对应的数据文件 author Admin
  • Linux - CentOS 6 安装 JDK

    安装步骤如下 1 下载jdk1 7 0 55 jdk 7u55 linux i586 tar gz 2 卸载系统自带的开源JDK 查看是否安装JDK rpm qa grep java 显示如下信息 存在 执行下面代码删除 rpm e jav
  • 智能营销增益模型(Uplift Modeling)实践整理

    一 uplift 思想 因果推断 常用的点击率预测模型 称为响应模型 response model 即预测用户看到商品 Treatment 后点击 购买 的概率 但在营销的发放优惠券这种场景下 很自然会想到 用户是本来就有购买的意愿还是因为
  • Java代码写好后怎么运行?

    对于很多Java初学者来说 编写好自己的第一个程序能够运行起来是一件非常自豪的事情 那么你知道应该如何才能运行Java代码吗 今天小千就来给大家介绍一下 Java代码运行方法 1 首先需要确保你的电脑正确安装了Java环境并且环境变量都配置
  • 设计模式的六大原则

    目的 高内聚 低耦合 基于 对接口编程而不是对实现编程 优先使用对象组合而不是继承 1 开闭原则 Open Close Principle 开闭原则的意思是 对扩展开放 对修改关闭 在程序需要进行拓展的时候 不能去修改原有的代码 实现一个热
  • STM32 PWM捕获方式:两种思想

    一 利用系统自带PWM捕获 1 代码 初始化 c include capture h include stm32f10x h void Capture Init PA7 gt TIM3 CH2 GPIO InitTypeDef GPIO I
  • springboot性能优化

    SpringBoot性能调优有三种方案 SpringBoot 是一个快速开发框架 能够快速的整合第三方框架 简化XML配置 全部采用注解形式 内置Tomcat容器 帮助开发者能够实现快速开发 SpringBoot的Web组件 默认集成的是S
  • 软件测试:白盒测试

    一 定义 白盒测试又称结构测试 透明盒测试 逻辑驱动测试或基于代码的测试 白盒测试是一种测试用例设计方法 盒子指的是被测试的软件 白盒指的是盒子是可视的 白盒 法全面了解程序内部逻辑结构 对所有逻辑路径进行测试 白盒 法是穷举路径测试 在使
  • webpack5搭建vue环境

    webpack5搭建vue环境 0 前言 安装 webpack webpack cli 开始我是全局安装进行测试 后期项目中最好用局部安装 不同项目使用的版本不同 熟悉 npm基本命令确保npm可以使用 1 webpack概念 中文官网 h
  • SQL语句的MINUS,INTERSECT和UNION ALL

    SQL语句中的三个关键字 MINUS 减去 INTERSECT 交集 和UNION ALL 并集 关于集合的概念 中学都应该学过 就不多说了 这三个关键字主要是对数据库的查询结果进行操作 正如其中文含义一样 两个查询 MINUS是从第一个查
  • vue实现下载文件和图片功能

    vue实现图片或文件下载功能 今天一个需求就是实现图片下载功能 刚开始以为很简单没有什么逻辑可写 就以为调用后端接口就可以了 调用之后发现有问 题 看来还是没有想象的那么简单 1 要自己创建一个a标签 以下就是下载功能的实现 这里是调用接口
  • 2.4 HTTP请求方法

    在客户端向服务器端发送请求时 需要确定使用的请求方法 请求方法表明对URL指定资源的操作方式 服务器会根据不同的请求方法进行不同的响应 在HTTP 1 1中 共定义了8种请求方法 具体如下 GET 请求指定的内容并返回 POST 向指定资源
  • 分布式缓存的切片模式-hash一致性切片

    文章目录 一 为什么使用缓存 二 为什么使用分布式 三 使用什么模式 四 常规切片模式的弊端 五 更加犀利的切片模式 hash一致性切片 六 不完美的数据倾斜以及解决方案 6 1 数据倾斜 6 2 解决办法 一 为什么使用缓存 当前 我们通
  • 基于stm32的keil调试

    目录 基于stm32的keil调试 前言 实验目的 问题 debug 定位问题 解决 总结 基于stm32的keil调试 本文目标 基于stm32的keil调试 按照本文的描述 应该可以跑通实验并举一反三 先决条件 装有编译和集成的开发环境
  • 06_个人注释版本(01版本)GTK播放器__基于Linux系统下的mplayer播放器

    include
  • 计算机视觉结合深度学习项目-智能停车场空车位实时识别

    欢迎来到本博客 本次博客内容将继续讲解关于OpenCV的相关知识 作者简介 目前计算机研究生在读 主要研究方向是人工智能和群智能算法方向 目前熟悉python网页爬虫 机器学习 计算机视觉 OpenCV 群智能算法 然后正在学习深度学习的相
  • Android框架源码解析之(五)Retrofit

    源码地址 https github com square retrofit Retrofit源码结构 可以看出Retrofit是使用idea maven依赖编写的Java工程 并不是一个Android 工程 Retrofit的简单使用 1