Retrofit2源码解析——网络调用流程(下)

Retrofit2源码解析系列

  • Retrofit2源码解析(一)
  • Retrofit2源码解析——网络调用流程(上)

本文基于Retrofit2的2.4.0版本

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
复制代码

上次我们分析到网络请求是通过OkHttpCall类来完成的,下面我们就来分析下OkHttpCall类。

final class OkHttpCall<T> implements Call<T> {...@Overridepublic void enqueue(final Callback<T> callback) {checkNotNull(callback, "callback == null");okhttp3.Call call;Throwable failure;synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;call = rawCall;failure = creationFailure;if (call == null && failure == null) {try {//调用createRawCall创建OkHttp3的Callcall = rawCall = createRawCall();} catch (Throwable t) {throwIfFatal(t);failure = creationFailure = t;}}}...call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {Response<T> response;try {//解析返回的结果response = parseResponse(rawResponse);} catch (Throwable e) {callFailure(e);return;}try {callback.onResponse(OkHttpCall.this, response);} catch (Throwable t) {t.printStackTrace();}}@Overridepublic void onFailure(okhttp3.Call call, IOException e) {callFailure(e);}private void callFailure(Throwable e) {try {callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {t.printStackTrace();}}});}...
}
复制代码

OkHttpCall的enqueue方法主要干了2件事,一个是创建OkHttp3的Call用于执行网络请求;另一个是解析返回的结果并回调。下面我们来看看创建OkHttp3的Call的过程

//OkHttpCall.class
private okhttp3.Call createRawCall() throws IOException {okhttp3.Call call = serviceMethod.toCall(args);if (call == null) {throw new NullPointerException("Call.Factory returned null.");}return call;
}
复制代码

可以发现是通过serviceMethod的toCall方法来创建的

//ServiceMethod.class
okhttp3.Call toCall(@Nullable Object... args) throws IOException {RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,contentType, hasBody, isFormEncoded, isMultipart);...for (int p = 0; p < argumentCount; p++) {handlers[p].apply(requestBuilder, args[p]);}//最后调用OkHttpClient的newCall方法返回Callreturn callFactory.newCall(requestBuilder.build());
}
复制代码

ServiceMethod的toCall方法也是通过OkHttpClient的newCall方法来返回Call的。

在我们通过OkHttpClient请求得到结果后,我们还需要将返回的结果Response解析成我们接口需要的实体类型,这就需要用到我们在创建Retrofit时设置的ConverterFactory了,比如GsonConverterFactory。

//OkHttpCall.class
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {ResponseBody rawBody = rawResponse.body();rawResponse = rawResponse.newBuilder().body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();...ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);try {//通过serviceMethod的toResponse方法解析T body = serviceMethod.toResponse(catchingBody);return Response.success(body, rawResponse);} catch (RuntimeException e) {catchingBody.throwIfCaught();throw e;}
}
复制代码

OkHttpCall的parseResponse方法调用的是serviceMethod的toResponse方法来解析返回的结果。

//ServiceMethod.class
R toResponse(ResponseBody body) throws IOException {return responseConverter.convert(body);
}
复制代码

在ServiceMethod中最后调用responseConverter的convert方法来转换返回的结果。这个responseConverter和上面分析的CallAdapter的确定过程一样,也是在ServiceMethod的build方法中,通过调用retrofit的requestBodyConverter方法遍历我们传入的ConverterFactory,直到找到合适的。

//Retrofit.class
public <T> Converter<T, RequestBody> requestBodyConverter(Type type,Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
}public <T> Converter<T, RequestBody> nextRequestBodyConverter(@Nullable Converter.Factory skipPast, Type type, Annotation[] parameterAnnotations,Annotation[] methodAnnotations) {...int start = converterFactories.indexOf(skipPast) + 1;for (int i = start, count = converterFactories.size(); i < count; i++) {Converter.Factory factory = converterFactories.get(i);Converter<?, RequestBody> converter =factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);if (converter != null) {//noinspection uncheckedreturn (Converter<T, RequestBody>) converter;}}...
}
复制代码

需要注意的是在创建Retrofit时默认添加了一个BuiltInConverters,这个是Retrofit为我们提供一个默认的responseConverter,它主要处理的是返回类型是ResponseBody和Void的情况。

final class BuiltInConverters extends Converter.Factory {@Overridepublic Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,Retrofit retrofit) {if (type == ResponseBody.class) {return Utils.isAnnotationPresent(annotations, Streaming.class)? StreamingResponseBodyConverter.INSTANCE: BufferingResponseBodyConverter.INSTANCE;}if (type == Void.class) {return VoidResponseBodyConverter.INSTANCE;}return null;}...
}
复制代码

因为我们一般返回值类型都是具体的实体类型,所以我们需要添加自己的responseConverter,一般也就是GsonConverterFactory了。

至此,网络调用的后半部分流程也清楚了:

我们调用Call对象的enqueue方法发起异步请求时,实际上调用的是OkHttpCall对应的enqueue方法。OkHttpCall会先调用ServiceMethod类的toCall方法利用OkHttpClient的newCall方法创建OkHttp3的call对象,然后利用这个call对象执行具体的网络请求。在网络请求返回成功以后会调用ServiceMethod类的toResponse方法利用我们设置的responseConverter将返回结果转换成我们需要的类型,然后通过我们设置的回调或是默认的回调方法,将结果回调回主线程,从而完成整个请求过程。

总结

Retrofit2的网络调用的整个流程我们已经分析完了。通过这次分析,我们可以看到Retrofit2中最主要的就是3个类:Retrofit、ServiceMethod和OkHttpCall。这三个类指责明确,相互配合共同完成整个网络调用的流程。

(1)Retrofit负责供外部初始化和定制,保存CallAdapter的列表和ResponseConverterFactory列表。

(2)ServiceMethod对应每一个接口方法的信息,包括解析注解和参数等,同时它也是连接Retrofit和OkHttpCall的桥梁。ServiceMethod中保存着当前接口对应方法所需要的CallAdapter和ResponseConverter。利用CallAdapter将OkHttpCall转换成接口需要的类型,供接口调用。利用toResponse方法让OkHttpCall调用ResponseConverter解析网络请求返回的结果。

(3)OkHttpCall则是用来执行具体网络请求。Retrofit2没有直接使用OkHttp3的Call接口,而是有自己的Call接口。在OkHttpCall内部通过组合的方法持有OkHttp3的Call接口,并通过ServiceMethod的toCall方法得到OkHttp3的call来进行网络请求,减少对OkHttp3的耦合。


                    欢迎关注我的微信公众号,和我一起每天进步一点点!
复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/253530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring EL 实现ref的效果

之前学习basic的时候有个疑问就是不知道如何实现bean中引用其他的bean的属性&#xff0c;当时是用ref来实现对其他bean的引用&#xff0c;但是ref必需引用的是一个常量。所以这种方式来实现对其他bean中的属性的引用是不合理的。 当我看到Spring Expression Language时发现原来…

2021手机CIS技术趋势总结

手机摄像头CIS&#xff08;CMOS图像传感器&#xff09;自从突破1亿像素以后&#xff0c;再谈像素数量增大&#xff0c;似乎已经很难让市场产生激烈反应了。这两年电子工程专辑对于手机摄像头CIS&#xff0c;以及更多领域不同类型的图像/视觉传感器&#xff08;如ToF、基于事件的…

关于Unity中NGUI的背包实现之Scrollview(基于Camera)

基于UIPanel的scrollview实现方式在移动设备上的性能不如基于camera的方式。因为UIPanel的scrollview实现方式要渲染很多的道具图&#xff0c;性能自然就降低了。如果是用第二个摄像机camera的方式&#xff0c;物体并没有动&#xff0c;只是拖动第二个摄像机摄像机&#xff0c;…

YUV422/420 format

(在本文中&#xff0c;U 一词相当于 Cb&#xff0c;V 一词相当于 Cr。) YUV422 format as shown below 4:2:2 表示 2:1 的水平取样&#xff0c;没有垂直下采样 YUV420 format as shown below4:2:0 表示 2:1 的水平取样&#xff0c;2:1 的垂直下采样. YUV4:2:0并不是说只有U&…

vue部署问题

history模式配置后刷新404的解决办法! 第一种 nginx配置 在usr/local/nginx/conf/vhost 下 域名.conf配置文件修改或添加 第一种方案server {##在server下添加或在location里面添加以下代码location / {if (!-e $request_filename) {rewrite ^(.*)$ /index.html?s$1 last…

位域

有些信息在存储时&#xff0c;并不需要占用一个完整的字节&#xff0c; 而只需占几个或一个二进制位。例如在存放一个开关量时&#xff0c;只有0和1 两种状态&#xff0c; 用一位二进位即可。为了节省存储空间&#xff0c;并使处理简便&#xff0c;C语言又提供了一种数据结构&a…

数字后端——ECO

目录 一、概述 二、ECO分类 1、按时间节点 1&#xff09;流片前的ECO 2&#xff09;流片过程的ECO 3&#xff09;流片后的ECO 2、按网表是否改变 1&#xff09;功能ECO 2&#xff09;时序ECO 三、ECO处理内容 1、设计规则违例 1&#xff09;提升标准单元驱动力 2…

Uncaught TypeError: Cannot read property 'length' of null错误怎么处理?

Uncaught TypeError: Cannot read property length of null 错误怎么处理&#xff1f; 1.可能是返回的datagrid数据格式有问题&#xff0c;比如{"total":0,"rows":null}&#xff0c;改为{"total":0,"rows":"[]"}就可以了 if…

电视百科常识 九大视频接口全接触

1 射频 天线和模拟闭路连接电视机就是采用射频&#xff08;RF&#xff09;接口。作为最常见的视频连接方式&#xff0c;它可同时传输模拟视频以及音频信号。RF接口传输的是视频和音频混合编码后的信号&#xff0c;显示设备的电路将混合编码信号进行一系列分离、解码在输出成像。…

tracert路由检测命令使用方法

很多客户网站无法访问的时候都会第一时间怀疑是虚拟主机有问题了&#xff0c;其实大多时候网站无法访问和很多因素相关&#xff0c;包括自己的网络、计算机设置、省际路由等等&#xff1b; 那么这里我就简单讲下如何利用DOS下的命令检测你的计算机到服务器之间的路由是否通畅&a…

数字后端——物理单元介绍

物理单元&#xff08; physical cell&#xff09;指没有逻辑功能但是具有物理实现功能的标准单元&#xff0c; 用于抑制芯片生产过程中的各类物理效应&#xff0c; 保证芯片生产后能够正常工作 。硬核位置确 定后&#xff0c;需要插入物理单元消除影响芯片工作的物 效应&#x…

vue双向数据绑定的原理

有关双向数据绑定的原理 最近两次面试的时候&#xff0c;被问到了vue中双向数据绑定的原理&#xff0c;因为初学不精&#xff0c;只是使用而没有深入研究&#xff0c;所以答不出来。之后就在网上查找了别人写的博客&#xff0c;学习一下。 下面是博客园一篇博客&#xff0c;以及…

求职网站总结

最近忙着要找份工作。毕业半年多就辞职&#xff0c;也是尴尬。 这里记录一些求职网站和找工作的一些经验。主要参考了三个知乎问题&#xff1a;怎么在互联网上找工作&#xff1f;&#xff0c;招聘网站&#xff0c;哪个靠谱&#xff1f;和哪个求职网站&#xff08;app&#xff0…

FTP命令解析

FTP命令详解FTP命令是Internet用户使用最频繁的命令之一&#xff0c;不论是在DOS还是UNIX操作系统下使用FTP&#xff0c;都会遇到大量的FTP内部命令。熟悉并灵活应用FTP的内部命令&#xff0c;可以大大方便使用者&#xff0c;并收到事半功倍之效。   FTP的命令行格式为&…

深入Java内存模型

你可以在网上找到一大堆资料让你了解JMM是什么东西&#xff0c;但大多在你看完后仍然会有很多疑问。happen-before是怎么工作的呢&#xff1f;用volatile会导致缓存的丢弃吗&#xff1f;为什么我们从一开始就需要内存模型&#xff1f; 通过这篇文章&#xff0c;读者可以学习到足…

Matlab 使用GPU加速 转载

在matlab中使用GPU加速&#xff0c;来加速矩阵运算。 首先如前面所说&#xff0c;并不是所有GPU都能在maltab中进行加速的&#xff0c;貌似只有NVDIA的显卡可以吧。 硬件&#xff1a;GeForce GTX 980 软件&#xff1a;Matlab 2015a &#xff08;Matlab 2012以后的版本才带有GP…

数字后端——可制造性设计

随着集成电路制造工艺技术的迅速发展&#xff0c;集成电路集成度迅速攀升&#xff0c;制造流程及工艺步骤日趋复杂&#xff0c;工艺尺寸也在不断缩小。集成电路可制造性设计&#xff08;Design For Manufacturability,DFM&#xff09; 以直接提升集成电路芯片的良品率及降低芯片…

Cloudstack安装(二)

Cloudstack安装 官方文档参考&#xff1a; http://docs.cloudstack.apache.org/projects/cloudstack-installation/en/4.9/qig.html#environment Cloudstack主要分Management和Agent两部分。 系统版本&#xff1a;CentOS 6.8 Management&#xff1a; cpu1&#xff0c;ram 2048M…

Pycharm 输出中文或打印中文乱码现象的解决办法

转载地址&#xff1a;https://www.cnblogs.com/Bro-Young/p/5920884.html 1. 确保文件开头加上以下代码&#xff1a; 1 # -*- coding:utf-8 -*- 还可以加上 1 import sys 2 reload(sys) 3 sys.setdefaultencoding(utf-8) 确保以下。 如果还是没有解决中文乱码&#xff0c;那么进…

计算机系统结构——概述

计算机的实现包括两个方面&#xff1a;组成和硬件。组成一词包含了计算机设计的高阶内容&#xff0c;例如存储器系统&#xff0c;存储器互连&#xff0c;设计内部处理器 CPU &#xff08;中央处理器——算术、逻辑、分支和数据传送功能都在内部实现&#xff09;。有时也用微体系…