Retrofit核心源码分析(三)- Call逻辑分析和扩展机制
在前面的两篇文章中,我们已经对 Retrofit 的注解解析、动态代理、网络请求和响应处理机制有了一定的了解。在这篇文章中,我们将深入分析 Retrofit 的 Call 逻辑,并介绍 Retrofit 的扩展机制。
一、Call 逻辑分析
Call 是 Retrofit 中最基本的操作单元,它代表一个 HTTP 请求。在 Retrofit 中,我们通过接口定义请求的方式,并通过动态代理生成接口的实现类。这个实现类中的方法都会返回一个 Call 对象,通过这个对象我们可以发起网络请求,并获得响应结果。
Retrofit 的 Call 逻辑可以用以下代码来描述:
public interface Call<T> {Response<T> execute() throws IOException;void enqueue(Callback<T> callback);void cancel();boolean isExecuted();boolean isCanceled();Call<T> clone();
}
在这个接口中,我们可以看到 Retrofit 定义了五个方法,分别是 execute、enqueue、cancel、isExecuted和isCanceled。我们来逐一分析这些方法的作用。
execute 方法
execute方法用于同步发起 HTTP 请求,当我们调用execute()方法时,它会立即发起网络请求,并且阻塞当前线程直到请求结束,最后返回一个响应对象Response。这种方式适用于简单的网络请求,但不适合于在主线程中执行请求,因为会阻塞 UI 线程。
以下是execute()方法的部分代码:
@Override
public Response<T> execute() throws IOException {synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;}captureCallStackTrace();try {client.dispatcher().executed(this);Response<T> result = getResponseWithInterceptorChain();if (result == null) throw new IOException("Canceled");return result;} catch (IOException e) {throw timeoutExit(e);} finally {client.dispatcher().finished(this);}
}
上述代码中,我们可以看到该方法首先会检查当前请求是否已经执行过,如果已经执行过则会抛出一个异常。然后会将该请求加入到Dispatcher中的执行队列中。接下来会调用getResponseWithInterceptorChain()方法来获取响应结果。如果结果为null,则表示请求已经被取消,否则返回响应结果。最后,会将该请求从执行队列中移除。
enqueue 方法
enqueue方法用于异步发起 HTTP 请求,它会在后台线程中发起网络请求,并在请求结束后将结果返回到主线程中。因此,enqueue()方法适用于在主线程中执行网络请求。
以下是enqueue()方法的部分代码:
@Override
public void enqueue(Callback<T> callback) {synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;}captureCallStackTrace();client.dispatcher().enqueue(new AsyncCall(callback));
}
上述代码中,我们可以看到该方法首先会检查当前请求是否已经执行过,如果已经执行过则会抛出一个异常。然后会将该请求封装成一个AsyncCall对象,加入到Dispatcher中的异步队列中。
AsyncCall继承自RealCall,它实现了Runnable接口,表示可以在后台线程中执行。在AsyncCall中,我们可以看到它会在后台线程中执行网络请求,并将结果通过Callback回调返回到主线程中。
final class AsyncCall extends RealCall.AsyncCall {private final Callback<T> responseCallback;AsyncCall(Callback<T> responseCallback) {super();this.responseCallback = responseCallback;}@Override protected void execute() {boolean signalledCallback = false;try {Response<T> response = getResponseWithInterceptorChain();if (retryAndFollowUpInterceptor.isCanceled()) {signalledCallback = true;responseCallback.onFailure(RealCall.this, new IOException("Canceled"));} else {signalledCallback = true;responseCallback.onResponse(RealCall.this, response);}} catch (IOException e) {if (signalledCallback) {// Do not signal the callback twice!Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);} else {responseCallback.onFailure(RealCall.this, e);}} finally {client.dispatcher().finished(this);}}@Override public String toString() {return RealCall.this.toString();}
}
在AsyncCall中,首先会调用getResponseWithInterceptorChain()方法来获取响应结果。如果请求被取消,则会调用onFailure()方法将结果返回到主线程中。否则,会调用onResponse()方法将结果返回到主线程中。无论请求成功或失败,都会调用finished()方法将该请求从异步队列中移除。
cancel 方法
cancel方法用于取消正在执行的请求。如果请求已经完成或已经取消,则该方法不会有任何效果。
isExecuted 方法
isExecuted方法用于判断请求是否已经执行。如果已经执行,则返回true,否则返回false。
isCanceled 方法
isCanceled方法用于判断请求是否已经取消。如果已经取消,则返回true,否则返回false。
clone 方法
clone方法用于创建一个当前 Call 对象的副本。这个方法一般用于发起多次相同的请求。
以上这些方法就构成了 Retrofit 的 Call 逻辑。在使用 Retrofit 时,我们一般会通过execute或enqueue方法发起 HTTP 请求,并通过传入的Callback对象来处理响应结果。
二、扩展机制
Retrofit 是一个非常灵活的网络请求库,它提供了很多扩展机制,让我们可以根据自己的需要来定制请求和响应处理逻辑。下面介绍一些常用的扩展机制。
Converter
Converter 用于将 HTTP 请求和响应的数据类型转换成 Java 对象。Retrofit 默认提供了两种 Converter:GsonConverter和JacksonConverter,它们分别使用Gson和Jackson库将数据类型转换成Java对象。如果我们需要使用其他类型的转换库,也可以自定义 Converter 来实现。
下面是一个自定义 Converter 的示例:
public class MyConverter implements Converter<ResponseBody, MyObject> {private Gson gson;public MyConverter(Gson gson) {this.gson = gson;}@Overridepublic MyObject convert(ResponseBody value) throws IOException {try {String json = value.string();MyObject obj = gson.fromJson(json, MyObject.class);return obj;} finally {value.close();}}
}
在上面的示例中,我们定义了一个MyConverter类,它实现了Converter接口,用于将ResponseBody类型的响应数据转换成MyObject类型的Java对象。在convert方法中,我们使用自己定义的转换逻辑来实现数据类型的转换。
Interceptor
Interceptor 用于对 HTTP 请求进行拦截和处理。在 Retrofit 中,我们可以通过 Interceptor 来对请求添加头信息、对响应进行缓存、打印请求日志等操作。Retrofit 提供了两种 Interceptor:OkHttpInterceptor和RetrofitInterceptor,它们分别用于拦截 OkHttp 的请求和 Retrofit 的请求。我们也可以自定义 Interceptor 来实现自己的拦截逻辑。
下面是一个自定义 Interceptor 的示例:
public class MyInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {// 在这里可以对请求进行拦截和处理Request request = chain.request();Response response = chain.proceed(request);// 在这里可以对响应进行处理return response;}
}
在上面的示例中,我们定义了一个MyInterceptor类,它实现了Interceptor接口,用于对请求和响应进行拦截和处理。在intercept方法中,我们可以对请求进行处理,然后使用chain.proceed(request)方法将请求传递给下一个拦截器或处理器,最终获得响应结果并进行处理。
CallAdapter
CallAdapter 用于将 Call 对象转换成另一种类型的对象。在 Retrofit 中,我们可以通过 CallAdapter 将 Call 对象转换成 RxJava 的 Observable 对象,也可以将 Call 对象转换成 LiveData 对象。Retrofit 提供了两种CallAdapter:RxJavaCallAdapter和LiveDataCallAdapter,它们分别用于将 Call 对象转换成 RxJava 的 Observable 对象和 LiveData 对象。我们也可以自定义 CallAdapter 来实现自己的转换逻辑。
下面是一个自定义 CallAdapter 的示例:
public class MyCallAdapter<T> implements CallAdapter<T, MyResult<T>> {@Overridepublic Type responseType() {// 返回响应数据的类型return new ParameterizedTypeImpl(MyResult.class, new Type[]{T.class});}@Overridepublic MyResult<T> adapt(Call<T> call) {// 在这里可以对Call对象进行处理,并返回自定义的结果对象try {Response<T> response = call.execute();return new MyResult<>(response.body(), response.code(), response.message());} catch (IOException e) {return new MyResult<>(e);}}
}
在上面的示例中,我们定义了一个MyCallAdapter类,它实现了CallAdapter接口,用于将Call对象转换成MyResult对象。在responseType方法中,我们返回了响应数据的类型,它是一个ParameterizedTypeImpl类型的对象,用于表示MyResult<T>类型。在adapt方法中,我们对Call对象进行处理,并返回自定义的MyResult对象,它包含了响应数据、状态码和错误信息。
总结
在本文中,我们深入分析了 Retrofit 的核心源码,特别是 Call 逻辑的实现细节,以及 Retrofit 的三个扩展机制:Converter、Interceptor 和 CallAdapter。
通过本文的学习,我们可以更加深入地理解 Retrofit 的工作原理,并可以根据需求自定义扩展 Retrofit 的功能。同时,本文提供了三个扩展机制的示例代码,可以帮助读者更加深入地了解 Retrofit 的扩展机制的使用。
相关文章:
Retrofit核心源码分析(三)- Call逻辑分析和扩展机制
在前面的两篇文章中,我们已经对 Retrofit 的注解解析、动态代理、网络请求和响应处理机制有了一定的了解。在这篇文章中,我们将深入分析 Retrofit 的 Call 逻辑,并介绍 Retrofit 的扩展机制。 一、Call 逻辑分析 Call 是 Retrofit 中最基本…...
源码分析spring如和对@Component注解进行BeanDefinition注册的
Spring ioc主要职责为依赖进行处理(依赖注入、依赖查找)、容器以及托管的(java bean、资源配置、事件)资源声明周期管理;在ioc容器启动对元信息进行读取(比如xml bean注解等)、事件管理、国际化等处理;首先…...
C语言--字符串函数1
目录前言strlenstrlen的模拟实现strcpystrcatstrcat的模拟实现strcmpstrcmp的模拟实现strncpystrncatstrncmpstrstrstrchr和strrchrstrstr的模拟实现前言 本章我们将重点介绍处理字符和字符串的库函数的使用和注意事项。 strlen 我们先来看一个我们最熟悉的求字符串长度的库…...
Webstorm使用、nginx启动、FinalShell使用
文章目录 主题设置FinalShellFinalShell nginx 启动历史命令Nginx页面发布配置Webstorm的一些常用快捷键代码生成字体大小修改Webstorm - gitCode 代码拉取webstorm 汉化webstorm导致CPU占用率高方法一 【忽略node_modules】方法二 【设置 - 代码编辑 - 快速预览文档 - 关闭】主…...
源码分析Spring @Configuration注解如何巧夺天空,偷梁换柱。
前言 回想起五年前的一次面试,面试官问Configuration注解和Component注解有什么区别?记得当时的回答是: 相同点:Configuration注解继承于Component注解,都可以用来通过ClassPathBeanDefinitionScanner装载Spring bean…...
vector的使用及模拟实现
目录 一.vector的介绍及使用 1.vector的介绍 2.vector的使用 1.vector的定义 2.vector iterator的使用 3. vector 空间增长问题 4.vector 增删查改 3.vector 迭代器失效问题(重点) 1. 会引起其底层空间改变的操作 2.指定位置元素的删除操作--erase 3. Li…...
“华为杯”研究生数学建模竞赛2007年-【华为杯】A题:基于自助法和核密度估计的膳食暴露评估模型(附获奖论文)
赛题描述 我国是一个拥有13亿人口的发展中国家,每天都在消费大量的各种食品,这批食品是由成千上万的食品加工厂、不可计数的小作坊、几亿农民生产出来的,并且经过较多的中间环节和长途运输后才为广大群众所消费,加之近年来我国经济发展迅速而环境治理没有能够完全跟上,以…...
刷题(第三周)
目录 [CISCN2021 Quals]upload [羊城杯 2020]EasySer [网鼎杯 2020 青龙组]notes [SWPU2019]Web4 [Black Watch 入群题]Web [HFCTF2020]BabyUpload [CISCN2021 Quals]upload 打开界面以后,发现直接给出了源码 <?php if (!isset($_GET["ctf"]))…...
新C++(14):移动语义与右值引用
当你在学习语言的时候,是否经常听到过一种说法,""左边的叫做左值,""右边的叫做右值。这句话对吗?从某种意义上来说,这句话只是说对了一部分。---前言一、什么是左右值?通常认为:左值是一个表示数据的表达式(…...
TCP相关概念
目录 一.滑动窗口 1.1概念 1.2滑动窗口存在的意义 1.3 滑动窗口的大小变化 1.4丢包问题 二.拥塞控制 三.延迟应答 四.捎带应答 五.面向字节流 六.粘包问题 七.TIME_WAIT状态 八.listen第2个参数 九.TCP总结 一.滑动窗口 1.1概念 概念:双方在进行通信时&a…...
MySQL锁篇
MySQL锁篇 一、一条update语句 我们的故事继续发展,我们还是使用t这个表: CREATE TABLE t (id INT PRIMARY KEY,c VARCHAR(100) ) EngineInnoDB CHARSETutf8;现在表里的数据就是这样的: mysql> SELECT * FROM t; —------- | id | c | —…...
SWF (Simple Workflow Service)简介
Amazon Simple Workflow Service (Amazon SWF) 提供了给应用程序异步、分布式处理的流程工具。 SWF可以用在媒体处理、网站应用程序后端、商业流程、数据分析和一系列定义好的任务上。 举个例子,下图表明了一个电商网站的工作流程,其中涉及了程序执行的…...
java(Class 常用方法 获取Class对象六种方式 动态和静态加载 类加载流程)
ClassClass常用方法获取Class对象六种方式哪些类型有Class对象动态和静态加载类加载流程加载阶段连接阶段连接阶段-验证连接阶段-准备连接阶段-解析初始化阶段获取类结构信息Class常用方法 第一步:创建一个实体类 public class Car {public String brand "宝…...
【数据结构】线性表和顺序表
Yan-英杰的主页 悟已往之不谏 知来者之可追 目录 1.线性表 2.顺序表 2.1 静态顺序表 2.2 动态顺序表 2.3移除元素 1.线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线…...
Ubuntu数据库安装(mysql)
##1.下载mysql-apt-config_0.8.22-1_all.deb并且安装 wget https://dev.mysql.com/get/mysql-apt-config_0.8.22-1_all.deb sudo dpkg -i mysql-apt-config_0.8.22-1_all.deb##2.更新apt-updata sudo apt update##3.如果出现如下图情况执行以下命令 [外链图片转存失败,源站可…...
MyBatis-Plus的入门学习
MyBatis-Plus入门学习简介特性快速开始MyBatis-Plus的注解详解Tableld主键生成策略1、数据库自动增长 AUTO2、UUID3、Redis生成id4、MP主键自动生成TableNameTableField自动填充测试方法:update乐观锁select查所有根据id查多个id批量查询简单条件查询(通…...
华为OD机试题 - 内存池(JavaScript)
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:内存池题目输入输出示例一输入输出说明Code解题思路版权说明华为…...
数据库索引原理
数据库索引的作用是做数据的快速检索,而快速检索实现的本质是数据结构。像二叉树、红黑树、AVL树、B树、B树、哈希等数据结构都可以实现索引,但其中B树效率最高。MySQL数据库索引使用的是B树。二叉树:二叉树中,左子树比根节点小&a…...
字符函数和字符串函数详解(1)
目录前言strlen函数strlensizeofstrcpy函数strcat函数strcmp函数总结前言 最近要调整状态,写的文章质量不佳让大家失望,我现在也在反思我在做什么,我会什么,我学了什么。等我想明白的那天,我一定能跟大家顶峰相见的&a…...
【数据分析:工具篇】NumPy(1)NumPy介绍
【数据分析:工具篇】NumPy(1)NumPy介绍NumPy介绍NumPy的特点数组的基本操作创建数组索引和切片数组运算NumPy介绍 NumPy(Numerical Python)是Python的一个开源的科学计算库,它主要用于处理大规模的多维数组…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
