Retrofit2 笔记

2024-04-25 07:32
文章标签 笔记 retrofit2

本文主要是介绍Retrofit2 笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。

Retrofit的设计非常插件化而且轻量级,真的是非常高内聚而且低耦合

Retrofit的优势

首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂;
其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;
再者,Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute即可完成;
最后,Retrofit更大自由度的支持我们自定义的业务逻辑,如自定义Converters。

从 Retrofit 的创建方法可以看出,使用的是 Builder 模式

Retrofit 中有如下的几个关键变量:(取自:https://segmentfault.com/a/1190000006767113)

//用于缓存解析出来的方法private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();//请求网络的OKHttp的工厂,默认是 OkHttpClientprivate final okhttp3.Call.Factory callFactory;//baseurlprivate final HttpUrl baseUrl;//请求网络得到的response的转换器的集合 默认会加入 BuiltInConverters     private final List<Converter.Factory> converterFactories;//把Call对象转换成其它类型private final List<CallAdapter.Factory> adapterFactories;//用于执行回调 Android中默认是 MainThreadExecutorprivate final Executor callbackExecutor;//是否需要立即解析接口中的方法private final boolean validateEagerly;

再看一下Retrofit 中的内部类 Builder 的 builder 方法:

public Retrofit build() {if (baseUrl == null) {throw new IllegalStateException("Base URL required.");}okhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {//默认创建一个 OkHttpClientcallFactory = new OkHttpClient();}Executor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {//Android 中返回的是 MainThreadExecutorcallbackExecutor = platform.defaultCallbackExecutor();}// Make a defensive copy of the adapters and add the default Call adapter.List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));// Make a defensive copy of the converters.List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly);
}

loadServiceMethod: 先到缓存中找,缓存中没有再去创建。这里创建了 ServiceMethod 对象。ServiceMethod 用于把接口方法的调用转换成一个 HTTP 请求。其实,在 ServiceMethod 中,会解析接口中方法的注解、参数等,它还有个 toRequest 方法,用于生成一个 Request 对象。这个 Request 对象就是 OkHttp 中的 Request,代表了一条网络请求(Retrofit 实际上把真正请求网络的操作交给了 OkHttp 执行)。

ServiceMethod loadServiceMethod(Method method) {ServiceMethod result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = new ServiceMethod.Builder(this, method).build();serviceMethodCache.put(method, result);}}return result;
}

OkHttpCall继承于 Call 接口。Call 是Retrofit 的基础接口,代表发送网络请求与响应调用,它包含下面几个接口方法:

Response<T> execute() throws IOException; //同步执行请求
void enqueue(Callback<T> callback); //异步执行请求,callback 用于回调
boolean isExecuted(); //是否执行过
void cancel(); //取消请求
boolean isCanceled(); //是否取消了
Call<T> clone(); //克隆一条请求
Request request(); //获取原始的request

OkHttpCall 是 Call 的一个实现类,它里面封装了 OkHttp 中的原生 Call,在这个类里面实现了 execute 以及 enqueue 等方法,其实是调用了 OkHttp 中原生 Call 的对应方法。

接下来把 OkHttpCall 传给 serviceMethod.callAdapter 对象,
那么 CallAdapter 是干嘛的呢?上面调用了adapt 方法,它是为了把一个 Call 转换成另一种类型,比如当 Retrofit 和 RxJava 结合使用的时候,接口中方法可以返回 Observable,这里相当于适配器模式。默认情况下得到的是一个 Call 对象,它是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);}});}});
}

在 enqueue 方法中,调用了 OkHttpCall 的 enqueue,所以这里相当于静态的代理模式。OkHttpCall 中的 enqueue 其实又调用了原生的 OkHttp 中的 enqueue,这里才真正发出了网络请求,部分代码如下:

@Override public void enqueue(final Callback<T> callback) {if (callback == null) throw new NullPointerException("callback == null");//真正请求网络的 callokhttp3.Call call;Throwable failure;synchronized (this) {if (executed) throw new IllegalStateException("Already executed.");executed = true;//省略了部分发代码...call = rawCall;//enqueue 异步执行call.enqueue(new okhttp3.Callback() {@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)throws IOException {Response<T> response;try {//解析数据 会用到 conveterFactory,把 response 转换为对应 Java 类型response = parseResponse(rawResponse);} catch (Throwable e) {callFailure(e);return;}callSuccess(response);}@Override public void onFailure(okhttp3.Call call, IOException e) {try {callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {t.printStackTrace();}}private void callFailure(Throwable e) {try {callback.onFailure(OkHttpCall.this, e);} catch (Throwable t) {t.printStackTrace();}}private void callSuccess(Response<T> response) {try {callback.onResponse(OkHttpCall.this, response);} catch (Throwable t) {t.printStackTrace();}}});
}

OkHttp 获取数据后,解析数据并回调callback响应的方法,一次网络请求便完成了。

Retrofit对象create方法返回Proxy.newProxyInstance动态代理

Java动态代理就是给了程序员一种可能:当你要调用某个Class的方法前或后,插入你想要执行的代码:
比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱(见http://www.jianshu.com/p/c1a3a881a144描述)。

/** Create an implementation of the API defined by the {@code service} interface. */
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();@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.如果是Object中的方法,直接调用if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}//为了兼容 Java8 平台,Android 中不会执行if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);}//下面是重点,解析方法ServiceMethod serviceMethod = loadServiceMethod(method);OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall);}
});
//取自:https://segmentfault.com/a/1190000006767113

为什么要使用动态代理?

Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");

create方法返回的api对象其实是一个动态代理对象,并不是一个真正的Api接口的implements产生的对象,当api对象调用getAuthor方法时会被动态代理拦截,然后调用Proxy.newProxyInstance方法中的InvocationHandler对象,它的invoke方法会传入3个参数:

  • Object proxy: 代理对象,不关心这个
  • Method method:调用的方法,就是getAuthor方法  
  • Object…args:方法的参数,就是”qinchao”

而Retrofit关心的就是method和它的参数args,接下去Retrofit就会用Java反射获取到getAuthor方法的注解信息,配合args参数,创建一个ServiceMethod对象

ServiceMethod就像是一个中央处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client.

使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送

OkHttpCall就是调用ServiceMethod获得一个可以执行的Request对象,然后等到Http请求返回后,再将response body传入ServiceMethod中,ServiceMethod就可以调用Converter接口将response body转成一个Java对象.

Retrofit会对解析过的请求进行缓存,就在Map

Retrofit的接口设计及其4个接口

Retrofit的设计非常插件化而且轻量级,真的是非常高内聚而且低耦合,这个和它的接口设计有关。Retrofit中定义了4个接口:

Callback<T> 请求数据返回的接口
Converter<F, T> 将HTTP返回的数据解析成Java对象
Call<T> 发送一个HTTP请求,默认的实现是OkHttpCall<T>
CallAdapter<T> 将Call对象转换成另一个对象,属性只有responseType一个,还有一个<R> T adapt(Call<R> call)方法,这个接口的实现类也只有一个DefaultCallAdapter

Call是Retrofit中重要的一个概念,代表被封装成单个请求/响应的交互行为

通过调用Retrofit2的execute(同步)或者enqueue(异步)方法,发送请求到网络服务器,并返回一个响应(Response)。

  • 独立的请求和响应模块
  • 从响应处理分离出请求创建
  • 每个实例只能使用一次。
  • Call可以被克隆。
  • 支持同步和异步方法。
  • 能够被取消。

好的代码就是依赖接口而不是实现

Retrofit 整个框架的代码不算太多,还是比较易读的。主要就是通过动态代理的方式把 Java 接口中的注解形式请求解析为响应的网络请求,然后交给 OkHttp 去执行。并且可以适配不同的 CallAdapter,可以方便与 RxJava 结合使用。

Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求

Retrofit的功能非常多的依赖Java反射,代码中其实还有很多细节,比如异常的捕获、抛出和处理,大量的Factory设计模式

Retrofit中接口设计的恰到好处,在你创建Retrofit对象时,让你有更多更灵活的方式去处理你的需求,比如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能

什么是好的代码?像Picasso和Retrofit这样的就是好的代码,扩展性强、低耦合、插件化


Retrofit源码的组成:

  • 一个retrofit2.http包,里面全部是定义HTTP请求的注解,比如GET、POST、PUT、DELETE、Headers、Path、Query等等
  • 余下的retrofit2包中十几个类和接口就是全部retrofit的代码了,代码真的很少,很简单,因为retrofit把网络请求这部分功能全部交给了okHttp了

以下描述粘贴自:
Retrofit2使用简介,Rabtman,7月 16, 2016

固定参数查询

@GET("tasks?pageNo=1")
Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks();// 方法调用
//service.getHistoryTasks();// 请求头
// GET http://.../tasks?pageNo=1 HTTP/1.1

动态参数(Query)

@GET("tasks")
Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks(@Query("pageNo") int no);// 方法调用
//service.getHistoryTasks(2);// 请求头
// GET http://.../tasks?pageNo=2 HTTP/1.1

动态参数(QueryMap)

@GET("tasks")
Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks(@QueryMap Map<String,String> map);//方法调用
HashMap<String,String> params = new HashMap<>();
params.put("pageNo", "1");
params.put("pageSize", "10");
service.getHistoryTasks(params);// 请求头
// GET http://.../tasks?pageNo=1&pageSize=10 HTTP/1.1

固定头

@GET("tasks")
@Headers("Accept-Encoding: application/json")
Call<PageDataDTO<HistoryTaskDTO>> getHistoryTask();// 方法调用
//service.getHistoryTask();// 请求头
// GET http://.../tasks HTTP/1.1
// Accept-Encoding: application/json

动态头

@GET("tasks")
Call<PageDataDTO<HistoryTaskDTO>> getHistoryTask(@Header("Location") String location);// 方法调用
//service.getHistoryTask("china");// 请求头
// GET http://.../tasks HTTP/1.1
// Location: china

路径替换

@GET("{token}/account/total/fetch")
Call<ResponseBody<String>> getTotal(@Path("token") String token);//方法调用
//service.getTotal("fd88b2ff");// 请求头
// GET http://.../fd88b2ff/account/total/fetch HTTP/1.1
POST请求(无BODY)//POST请求无BODY
@POST("auth")
Call<UserDTO> userLogin();

POST请求(带BODY,)

//POST请求带BODY,其中的body对象将被Retrofit实例指定的转换器转换
@POST("auth")
Call<UserDTO> userLogin(@Body User user);
POST请求(表单传参,Field)@FormUrlEncoded
@POST("auth")
Call<UserDTO> userLogin( @Field("username") String username);

POST请求(表单传参,FieldMap)

@FormUrlEncoded
@POST("auth")
Call<UserDTO> userLogin(@FieldMap Map<String, String> params);//方法调用
HashMap<String,String> params = new HashMap<>();params.put("username", "15222222222");params.put("code", "123456");
service.userLogin(params);

动态URL

@FormUrlEncoded 
Call<UserDTO> getUserInfo(@Url String url);

参考链接:
Android 网络框架之Retrofit2使用详解及从源码中解析原理
What is the best library to make HTTP calls from Java/Android?
Comparison of Android networking libraries: OkHTTP, Retrofit, and Volley [closed]
Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android

再回头看,有如下几篇源码解析的好文章,list如下:
Retrofit2 源码解析 白瓦力 2015.12.13
Android Retrofit源码解析 segmentfault 然则 2016年08月30日发布
Retrofit2 源码解析 Dec 13 2015
Android 网络框架之Retrofit2使用详解及从源码中解析原理
使用Retrofit2.0+OkHttp3.0实现缓存处理, 2016-7-29
Android网络框架源码分析二—Retrofit,2016-1-3(包含与Volley对比)
开源项目分析android-open-project-analysis,包含流程图和类图
拆轮子系列:拆 Retrofit Posted by Piasy on June 25, 2016

这篇关于Retrofit2 笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/934059

相关文章

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

数学建模笔记—— 非线性规划

数学建模笔记—— 非线性规划 非线性规划1. 模型原理1.1 非线性规划的标准型1.2 非线性规划求解的Matlab函数 2. 典型例题3. matlab代码求解3.1 例1 一个简单示例3.2 例2 选址问题1. 第一问 线性规划2. 第二问 非线性规划 非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法。运筹学的一个重要分支。2

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

查看提交历史 —— Git 学习笔记 11

查看提交历史 查看提交历史 不带任何选项的git log-p选项--stat 选项--pretty=oneline选项--pretty=format选项git log常用选项列表参考资料 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的 工具是 git log 命令。 接下来的例子会用一个用于演示的 simplegit

记录每次更新到仓库 —— Git 学习笔记 10

记录每次更新到仓库 文章目录 文件的状态三个区域检查当前文件状态跟踪新文件取消跟踪(un-tracking)文件重新跟踪(re-tracking)文件暂存已修改文件忽略某些文件查看已暂存和未暂存的修改提交更新跳过暂存区删除文件移动文件参考资料 咱们接着很多天以前的 取得Git仓库 这篇文章继续说。 文件的状态 不管是通过哪种方法,现在我们已经有了一个仓库,并从这个仓

忽略某些文件 —— Git 学习笔记 05

忽略某些文件 忽略某些文件 通过.gitignore文件其他规则源如何选择规则源参考资料 对于某些文件,我们不希望把它们纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常它们都是些自动生成的文件,比如日志文件、编译过程中创建的临时文件等。 通过.gitignore文件 假设我们要忽略 lib.a 文件,那我们可以在 lib.a 所在目录下创建一个名为 .gi

取得 Git 仓库 —— Git 学习笔记 04

取得 Git 仓库 —— Git 学习笔记 04 我认为, Git 的学习分为两大块:一是工作区、索引、本地版本库之间的交互;二是本地版本库和远程版本库之间的交互。第一块是基础,第二块是难点。 下面,我们就围绕着第一部分内容来学习,先不考虑远程仓库,只考虑本地仓库。 怎样取得项目的 Git 仓库? 有两种取得 Git 项目仓库的方法。第一种是在本地创建一个新的仓库,第二种是把其他地方的某个

Git 的特点—— Git 学习笔记 02

文章目录 Git 简史Git 的特点直接记录快照,而非差异比较近乎所有操作都是本地执行保证完整性一般只添加数据 参考资料 Git 简史 众所周知,Linux 内核开源项目有着为数众多的参与者。这么多人在世界各地为 Linux 编写代码,那Linux 的代码是如何管理的呢?事实是在 2002 年以前,世界各地的开发者把源代码通过 diff 的方式发给 Linus,然后由 Linus