Retrofit核心源码分析(二)- 网络请求和响应处理
在上一篇文章中,我们详细分析了 Retrofit 中的注解解析和动态代理实现,本篇文章将继续深入研究 Retrofit 的核心源码,重点分析 Retrofit 如何进行网络请求和响应处理。
网络请求
在使用 Retrofit 发起网络请求时,我们可以通过定义一个接口并使用 Retrofit 的注解来描述这个接口中的请求,Retrofit 会自动生成一个实现该接口的代理对象。当我们调用这个代理对象的方法时,Retrofit 会根据注解的描述构建一个 Request 对象,并使用 OkHttp 将这个 Request 发送出去。
在 Retrofit 中,我们可以通过 Retrofit#execute 或 Retrofit#enqueue 方法来发送请求。这两个方法的区别在于,execute 方法会阻塞当前线程直到请求完成,而 enqueue 方法会将请求加入到 OkHttp 的请求队列中,并在请求完成时通过回调通知我们。
我们先来看一下 execute 方法的实现:
public <T> T execute(Call<T> call) throws IOException {Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());return (T) callAdapter(call, call.request().tag()).adapt(call).execute();
}
在这个方法中,首先会对接口进行校验,确保这个接口是有效的。然后我们会根据请求的 Tag 和 URL 来获取适配器callAdapter,并使用适配器来执行请求。
适配器的作用是将请求的参数适配成 OkHttp 能够识别的形式,并将 OkHttp 的响应适配成我们需要的形式。Retrofit 提供了一系列的适配器,包括 Call 适配器、RxJava 适配器、CompletableFuture 适配器等。
我们来看一下 callAdapter 方法的实现:
private CallAdapter<?, ?> callAdapter(Call<?> call, Object tag) {Type responseType = call.request().method().equals("HEAD")? Void.class: getParameterUpperBound(0, (ParameterizedType) call.request().tag());return callAdapter(tag, responseType);
}
在这个方法中,我们首先根据请求的方法来判断响应的类型,如果是 HEAD 方法,那么响应的类型就是 Void;否则我们会通过反射来获取请求的响应类型,并使用这个响应类型来获取适配器。
获取适配器的方法是 callAdapter:
public <R, T> CallAdapter<R, T> callAdapter(Object tag, Type returnType) {// ...for (CallAdapter.Factory factory : adapterFactories) {CallAdapter<?, ?> adapter = factory.get(returnType, annotations, this);if (adapter != null) {return (CallAdapter<R, T>) adapter;}}// ...
}
在这个方法中,我们会遍历所有的适配器工厂,尝试获取适配器。在获取适配器时,我们会将请求的响应类型、注解和 Retrofit 实例作为参数传入。每个适配器工厂都会判断这些参数是否符合自己的适配条件,如果符合,就返回一个适配器实例,否则返回 null。在遍历完所有的适配器工厂之后,如果还没有获取到适配器,那么就会抛出一个异常。
获取到适配器之后,我们就可以使用适配器来执行请求了。在适配器中,我们会将请求参数转换成 OkHttp 的 Request 对象,并将 OkHttp 的 Response 对象转换成我们需要的响应类型。具体的实现可以参考 Retrofit 提供的 CallAdapter 接口。
对于 enqueue 方法,我们可以先来看一下 enqueue 方法的实现:
public <T> void enqueue(Call<T> call, Callback<T> callback) {Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());callAdapter(call, call.request().tag()).adapt(call).enqueue(new CallbackRunnable<>(callback));
}
在这个方法中,我们首先进行接口校验,然后根据请求的 Tag 和 URL 来获取适配器,并使用适配器来执行请求。不同的是,在 enqueue 方法中,我们将一个 Callback 对象作为参数传入适配器的 enqueue 方法中,以便在请求完成后回调通知我们。
在适配器中,我们可以看到 enqueue 方法的实现:
public void enqueue(final Callback<T> callback) {delegate.enqueue(new Callback<Response<T>>() {@Override public void onResponse(Call<Response<T>> call, Response<Response<T>> response) {Response<T> body;try {body = response.body();} catch (Throwable t) {if (response.code() == 204) {body = null;} else {callback.onFailure(call, t);return;}}if (response.isSuccessful()) {callback.onResponse(call, Response.success(body, response.raw()));} else {callback.onFailure(call, Response.error(response.errorBody(), response.raw()));}}@Override public void onFailure(Call<Response<T>> call, Throwable t) {callback.onFailure(call, t);}});
}
在这个方法中,我们会将传入的 Callback 对象转换成一个 Callback<Response<T>> 对象,并使用这个对象来调用 OkHttp 的 enqueue 方法。在请求完成后,我们会将 OkHttp 的 Response 对象转换成 Retrofit 的 Response 对象,并根据响应码来判断请求的结果。如果响应码表示请求成功,那么我们就调用 Callback 对象的 onResponse 方法;否则就调用 Callback 对象的 onFailure 方法。
响应处理
在 Retrofit 中,我们可以通过定义一个接口并使用注解来描述我们期望的请求格式和响应格式。例如,我们可以通过 @GET 注解来描述一个 GET 请求,使用 @Query 注解来描述请求参数,使用 @Body 注解来描述请求体,使用 @Headers 注解来描述请求头等。
在执行请求时,Retrofit 会根据这些注解来自动生成一个对应的请求对象,并将请求对象转换成 OkHttp 的 Request 对象。在接收响应时,Retrofit 会将 OkHttp 的 Response 对象转换成一个对应的响应对象,并将响应对象中的数据转换成我们需要的数据类型。这些转换工作是通过 Retrofit 的转换器来完成的,Retrofit 中默认提供了两个转换器:GsonConverterFactory 和 JacksonConverterFactory。我们也可以自定义一个转换器来实现我们期望的数据转换。
在 Retrofit 类的构造方法中,我们可以看到 Retrofit 默认使用了 Platform.get() 方法来获取当前运行平台的默认转换器工厂,并将其添加到 converterFactories 中。然后,我们可以使用 addConverterFactory 方法来添加自定义的转换器工厂。
public Retrofit(Builder builder) {// ...if (builder.converterFactories == null) {converterFactories.add(Platform.get().defaultConverterFactory());} else {converterFactories.addAll(builder.converterFactories);}// ...
}public interface Platform {// ...Converter.Factory defaultConverterFactory();
}
在execute方法中,我们会调用适配器的 adapt 方法来执行请求,并将返回的 Call 对象转换成一个响应对象。在转换过程中,我们会根据响应类型来选择对应的转换器来进行转换。具体的转换实现可以参考 Retrofit 提供的 Converter 接口和 Converter.Factory 接口。
public <T> T execute(Call<T> call) throws IOException {// ...Response<T> response = call.execute();if (response.isSuccessful()) {return response.body();} else {Converter<ResponseBody, ErrorResponse> converter = retrofit.responseBodyConverter(ErrorResponse.class, new Annotation[0]);throw new ApiException(converter.convert(response.errorBody()));}
}@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T adapt(Call<T> call) {return (T) new OkHttpCall<>(requestFactory, callFactory, converter, call);
}public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {return nextResponseBodyConverter(null, type, annotations);
}public <T> Converter<ResponseBody, T> nextResponseBodyConverter(@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {Objects.requireNonNull(type, "type == null");Objects.requireNonNull(annotations, "annotations == null");int start = converterFactories.indexOf(skipPast) + 1;for (int i = start, count = converterFactories.size(); i < count; i++) {Converter<ResponseBody, ?> converter =converterFactories.get(i).responseBodyConverter(type, annotations, this);if (converter != null) {return (Converter<ResponseBody, T>) converter;}}throw new IllegalArgumentException("Could not locate ResponseBody converter for " + type + " with annotations " + Arrays.toString(annotations));
}
以上是 Retrofit 中处理响应的核心代码。当我们执行一个请求时,Retrofit 会先将请求转换成 OkHttp 的 Request 对象并发送出去,然后等待响应返回。当响应返回时,Retrofit 会将响应转换成一个响应对象,并将响应对象中的数据转换成我们期望的数据类型。这个过程中,我们可以使用 Retrofit 提供的转换器来自定义数据的转换规则。
下面是一个示例,演示了如何使用 Retrofit 来发送一个 GET 请求并将响应中的 JSON 数据转换成一个 Java 对象:
public interface ApiService {@GET("users/{user}/repos")Call<List<Repo>> listRepos(@Path("user") String user);
}Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com/").addConverterFactory(GsonConverterFactory.create()).build();ApiService apiService = retrofit.create(ApiService.class);
Call<List<Repo>> call = apiService.listRepos("smallmarker");
List<Repo> repos = call.execute().body();
在上面的示例中,我们首先使用 Retrofit 构建器创建一个 Retrofit 实例,并指定了请求的基础 URL 和转换器工厂。然后,我们通过调用 create 方法来创建一个 ApiService 的代理对象。最后,我们调用 listRepos 方法来发送一个 GET 请求。
在上面的示例中,我们使用了 Retrofit 的 GsonConverterFactory 来将响应体中的 JSON 数据转换成 Java 对象。具体实现可以查看 Retrofit 提供的 GsonConverterFactory 类。
public final class GsonConverterFactory extends Converter.Factory {private final Gson gson;private GsonConverterFactory(Gson gson) {this.gson = gson;}public static GsonConverterFactory create() {return create(new Gson());}public static GsonConverterFactory create(Gson gson) {if (gson == null) throw new NullPointerException("gson == null");return new GsonConverterFactory(gson);}@Overridepublic @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));return new GsonResponseBodyConverter<>(gson, adapter);}@Overridepublic @Nullable Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,Annotation[] methodAnnotations, Retrofit retrofit) {TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));return new GsonRequestBodyConverter<>(gson, adapter);}
}
可以看到,GsonConverterFactory 继承了 Retrofit 的 Converter.Factory 类,并重写了其中的 responseBodyConverter 方法和 requestBodyConverter 方法。在 responseBodyConverter 方法中,我们将响应体中的 JSON 数据转换成 Java 对象,而在 requestBodyConverter 方法中,我们将 Java 对象转换成请求体中的 JSON 数据。
除了 GsonConverterFactory 以外,Retrofit 还提供了其他的转换器,如 JacksonConverterFactory、MoshiConverterFactory 等,我们可以根据需要选择适合自己的转换器。
总的来说,Retrofit 中网络请求和响应处理的核心代码非常简洁明了。我们只需要通过定义接口来描述请求和响应,然后使用 Retrofit 的动态代理机制来将接口转换成一个实际的实现类,并通过 Retrofit 的配置来指定请求和响应的转换器即可。这种方式大大简化了网络请求的流程,使得我们可以更加专注于业务逻辑的处理。
相关文章:
Retrofit核心源码分析(二)- 网络请求和响应处理
在上一篇文章中,我们详细分析了 Retrofit 中的注解解析和动态代理实现,本篇文章将继续深入研究 Retrofit 的核心源码,重点分析 Retrofit 如何进行网络请求和响应处理。 网络请求 在使用 Retrofit 发起网络请求时,我们可以通过定…...
STM32启动模式讲解与ICP下载电路
一、官方提供的启动模式说明硬件BOOT引脚接法表格从表格可以看出有三种启动模式,然后对应这不同的存储器启动,那我们现在疑问为啥有三种不能只有一种就好,还有存储器启动区域怎么区分,有些乱,带着这些疑问,…...
5款小巧好用的电脑软件,让你的工作生活更加高效!
不得不说良心好软件让大家好评连连,爱不释手,不像某些软件自带广告弹窗。这期就由我给大家安利几款电脑中的得力助手,看看你都用过几个? 1.桌面管理神器——Coodesker Coodesker是一款免费小巧、无广告,功能简单的桌…...
python线程池
假设我们必须多线程任务创建大量线程。 由于线程太多,因此可能会有很多性能问题,这在计算上会是最昂贵的。 一个主要问题可能是吞吐量受限。 我们可以通过创建一个线程池来解决这个问题。 一个线程池可以被定义为一组预先实例化和空闲的线程,…...
深入浅出PaddlePaddle函数——paddle.ones_like
分类目录:《深入浅出PaddlePaddle函数》总目录 相关文章: 深入浅出PaddlePaddle函数——paddle.Tensor 深入浅出PaddlePaddle函数——paddle.ones 深入浅出PaddlePaddle函数——paddle.zeros 深入浅出PaddlePaddle函数——paddle.full 深入浅出Padd…...
计算机组成原理(海明码效验)(3)-软件设计(二十四)
计算机组成原理(2)-软件设计(二十三)https://blog.csdn.net/ke1ying/article/details/129394115 一、总线 分为 内部总线、系统总线、外部总线。 内部总线:指芯片级别的总线,连接各个芯片。 系统总线&a…...
Linux2.2网络驱动程序编写
一.Linux系统设备驱动程序概述1.1 Linux设备驱动程序分类1.2 编写驱动程序的一些基本概念二.Linux系统网络设备驱动程序2.1 网络驱动程序的结构2.2 网络驱动程序的基本方法2.3 网络驱动程序中用到的数据结构2.4 常用的系统支持三.编写Linux网络驱动程序中可能遇到的问题3.1 中断…...
像素密度提升33%,Quest Pro动态注视点渲染原理详解
在Connect 2022上,Meta发布了Quest Pro,并首次在VR中引入动态注视点渲染(ETFR)功能,这是一种新型图形优化技术,特点是以用户注视点为中心,动态调节VR屏幕的清晰度(注视点中心最清晰、…...
【Linux实战篇】二、在Linux上部署各类软件
一、实战章节:在Linux上部署各类软件 二、MySQL数据库管理系统安装部署【简单】 简介 MySQL数据库管理系统(后续简称MySQL),是一款知名的数据库系统,其特点是:轻量、简单、功能丰富。 MySQL数据库可谓是…...
基于SpringBoot的学生会管理系统 源码
StudentUnionManagementSystem 基于SpringBoot的学生会管理系统 源码 链接 目录StudentUnionManagementSystem介绍软件架构使用说明1.页面登录2.首页3.成员信息管理4.角色信息管理5.权限管理6.活动管理7.文件管理8.活动展示介绍 学生会管理系统 SpringBoot Mybatis-plus shir…...
[league/glide]两行代码实现一套强大的图片处理HTTP服务
只要两行代码,就能实现类似对象存储云提供的基于参数的图片处理,比如裁剪、放大、水印、旋转等等。 我们经常使用第三方的对象存储服务,比如七牛云或阿里云,他们都提供了“智能媒体服务”,其实就是在链接上加上各种参…...
Kafka 消费者组
Kafka 消费者组Consumer数位移重平衡消费者组 (Consumer Group) : 可扩展且容错性的消费者机制 一个组内可能有多个消费者 (Consumer Instance) : 共享一个公共 ID (Group ID)组内的所有消费者协调消费订阅主题 (Subscribed Topics) 的所有分区 (Partition)每个分区只能由同个…...
高效学 C++|组合类的构造函数
设计好MyString类后,就可以像使用普通类型一样使用它了。例如,类的对象可以像普通的变量一样作为另一个类的数据成员。【例1】 MyString类的对象作为CStudent类的数据成员。1. //MyString类的定义省略 2. //注意:保留其构造函数、析构函数、…...
Java使用Springboot+Mybatis构建第一个项目
一、java安装:安装 Java1.8环境 maven3.6.1环境 Gradle-6.9.1环境 IntelliJ IDEA 2022.1.3 下载旗舰版(因为包含springboot)二、项目构建-数据库是sqlserver:1、打开idea,点击File->New->Project,选中…...
L2-007 家庭房产 L1-007 念数字
给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。 输入格式: 输入第一行给出一个正整数N(≤1000),随后N行,每行按下列格式给出一个人的房产: 编…...
1/4、1/2、整车悬架天棚主动控制仿真分析合集
目录 前言 1. 1/4悬架系统 1.1数学模型 1.2仿真分析 2. 1/2悬架系统 2.1数学模型 2.2仿真分析 3. 整车悬架系统 3.1数学模型 3.2仿真分析 4.总结 参考文献 前言 对于天棚控制相比大家不陌生,它是由美国的Karnopp提出,利用假设的与天棚固连…...
【微信小程序项目实战】TodoList-项目主体搭设(2)
目录JS 部分数据 dataonShow输入框双向绑定保存与读取添加新的待办事项完成待办事项删除待办事项WXML顶部输入框主体回到顶部按钮完整代码JSWXMLWXSSJS 部分 为便于分析各个组件的相互作用与原理,故先从 JS 入手,而后再完善 HTML 部分 以下所有代码&…...
23种设计模式-迭代器模式(安卓应用场景介绍)
迭代器模式是一种行为型设计模式,它允许你在不暴露集合对象内部结构的情况下遍历集合中所有元素。在本文中,我们将介绍迭代器模式的概念和原理,提供一个基于Java的示例,并探讨在Android应用程序开发中的实际应用。 迭代器模式的概…...
面试 - 软件工程体系
今天是我人生中的第二次面试,第一次面试到技术问题。 面试公司:无锡信捷电气股份有限公司 面试时间:2023 年 3 月 6 日 15:30 面试地点:西安工程大学(临潼校区)D-188 在技术面中,我表现的不是很…...
05-CSS
今日目标能够说出 为什么要用定位能够说出 定位的 4 种分类能够说出 4 种定位各自的特点能够说出 为什么常用子绝父相布局能够写出 淘宝轮播图布局能够说出 显示隐藏的 3 种方式以及区别1. 定位(position) 介绍1.1 为什么使用定位我们先来看一个效果,同时思考一下用…...
ESP32上给LVGL做个‘懒加载’:分页与动态读取大文本的实战对比(附代码)
ESP32上LVGL大文本显示优化:分页加载与动态读取的深度对比与实践 在嵌入式设备上处理大文本显示一直是开发者面临的挑战之一。当我们在ESP32这样的资源受限平台上使用LVGL(Light and Versatile Graphics Library)显示超长文本时,如…...
BiliTools跨平台哔哩哔哩工具箱:一站式B站资源管理终极解决方案
BiliTools跨平台哔哩哔哩工具箱:一站式B站资源管理终极解决方案 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持视频、音乐、番剧、课程下载……持续更新 项目地址: https://gitcode.com/GitHub_Trending/b…...
Spring PetClinic实战解析:从单体应用到云原生部署的5大架构亮点
Spring PetClinic实战解析:从单体应用到云原生部署的5大架构亮点 【免费下载链接】spring-petclinic A sample Spring-based application 项目地址: https://gitcode.com/gh_mirrors/sp/spring-petclinic 你是否遇到过这样的困境:在学习Spring框架…...
深入解析Shim在跨版本API兼容中的实战应用
1. 什么是Shim技术 第一次听到"Shim"这个词是在调试一个Flink连接Hive的项目时。当时Hive版本从2.3升级到3.1,本以为要重写大量代码,结果同事说"加个Shim就行了"。这种"神奇胶水"般的技术让我印象深刻。 Shim本质上是一种…...
Linux 内核模块编程入门
Linux 内核模块编程入门 内核模块的重要性 作为科技创业者,我深刻理解内核模块在系统开发中的灵活性和强大功能。内核模块允许我们在不重新编译整个内核的情况下,动态地添加或移除功能。这种机制不仅加快了开发迭代速度,还为产品定制化提供了…...
OpCore-Simplify:让黑苹果配置从复杂到简单的智能化革命
OpCore-Simplify:让黑苹果配置从复杂到简单的智能化革命 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 你是否曾为黑苹果(Hac…...
3个实用技巧:让Mermaid图表创作效率翻倍的秘密武器
3个实用技巧:让Mermaid图表创作效率翻倍的秘密武器 【免费下载链接】mermaid mermaid-js/mermaid: 是一个用于生成图表和流程图的 Markdown 渲染器,支持多种图表类型和丰富的样式。适合对 Markdown、图表和流程图以及想要使用 Markdown 绘制图表和流程图…...
终极指南:nanoGPT如何让每个人都能训练自己的AI语言模型?
终极指南:nanoGPT如何让每个人都能训练自己的AI语言模型? 【免费下载链接】nanoGPT The simplest, fastest repository for training/finetuning medium-sized GPTs. 项目地址: https://gitcode.com/GitHub_Trending/na/nanoGPT 想要训练自己的AI…...
Vue 3 Fragments:打破枷锁的组件化革命
Vue 3 Fragments:打破枷锁的组件化革命 在前端框架的演进史上,每一次对底层限制的突破,往往都伴随着开发体验的质的飞跃。Vue 3 中引入的 Fragments(片段) 特性,正是这样一场迟来的“解绑”革命。它彻底粉碎…...
质子交换膜燃料电池Comsol完整版仿真模型:涵盖两相流非等温雾状流道、液态水相变及扩散项,考...
质子交换膜燃料电池仿真Comsol完整版 虽然氢电发文量多了,但是氢电模型复杂程度和别的领域没法比,两相流非等温的氢燃料电池,跑通的都得好几千的,这个模型的流道和内侧都是多相流,这个里面是雾状流的流道,目…...
