前言
Retrofit A type-safe HTTP client for Android and Java
Retrofit,是一个基于http请求库二次封装的HTTP客户端,将 REST API 转换为 Java 接口。
基于注解,进一步解放了生产力,使得http请求就像调用方法一样简单,如丝般顺滑。
结构概览
项目结构整体分四个部分,Builder -> Proxy -> Invocation -> RawCall
这里我们把基于Retrofit的HTTP通信比做是邮递信件。
邮递信件
- 信封:当我们准备好信件之后,要在信封上写邮寄地址,收件人,可能还要备注勿折(是的,我暴露了我的年龄,如今很多人可能都没有过写信寄信的体验)。
- 邮递员:然后我们亲自去送信吗?No,我们把信投入邮箱,交给邮递员代为送信就行了。
- 邮局:然后邮递员会根据信封上的信息对信件进行分拣,寄信或收信均经由邮局统一处理
- 邮寄方式:最后就是交给运送单位送信了,空运或是陆运等。
基于Retrofit的HTTP通信
- Builder:当我们准备好数据之后,要指定服务端的通信地址,处理接口地址,请求方法,可能还要备注是否有body、是否是multipart。
- Proxy:然后通信的事交给代理去做,代理会帮你做好一系列的工作,比如注解解析,Call适配,以及请求调度等
- Invocation:这里负责调度同步或异步请求,请求装配和响应解析
- RawCall:这里就是具体的通信工具了,可选Okhttp等框架来做具体的Http通信。
来看看寄信和Retrofit之间的对比:
大概过程就是这样,邮递员会把信送出去,并在适合的时机把对方的回信取回来送给你,当然如果你的信件是表白情书,那也很可能会收不到回信的,毕竟表白成功的概率要看人品的。不要伤心,HTTP通信也会有时候收不到服务端的回信噢。
目录概览
官方 Javadoc
Retrofit的基本用法
让我们从基本用法开始,先看如何使用,顺着这个藤,摸摸如何实现的瓜。
用 Java 接口的方式定义一个HTTP API.
Retrofit 类生成一个 GitHubService 接口的实现实例.
Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.
GitHubService实例的每一个方法调用都支持同步或异步HTTP请求.
执行同步或异步HTTP请求,得到HTTP响应数据.
Retrofit的源码解析
首先我们心里要有个概念,Retrofit的核心关键词:注解、动态代理、转换器、适配器
Retrofit就是基于这四个关键词搭建起来的充分解耦,灵活,可插拔的优秀框架。
下面我们结合Retrofit设计图流程来解读代码。 还记得流程吗? Builder -> Proxy -> Invocation -> RawCall.
Flow - Builder
Retrofit.Builder()
.baseUrl("https://api.github.com/")
...
.build();
Tips.设计模式之Builder模式
基于Builder模式,装配一系列零部件,比如base请求地址,gson转换器,Rxjava适配器,HTTP请求client(比如装配OKHTTP)等。
返回一个装配了 callFactory,converterFactories,adapterFactories,callbackExecutor 和指定了 baseUrl 的 Retrofit 实例。
注:validateEagerly
,用于指定是否预先解析注解,加速接口访问效率。
Flow - Proxy
GitHubService service = retrofit.create(GitHubService.class);
我们知道,Java 接口是不可以直接 new 实例的,那么这个 create 方法看起来又像是返回了一个 GitHubService 接口类型的实现实例,这是怎么回事呢?我们来看下 create 的实现。
create方法主要就一个return,返回了一个Proxy.newProxyInstance生成的动态代理对象。原来这里是通过动态代理的方式生成了 GitHubService 接口的代理实例,那么后续 GitHubService 接口的方法都可以通过代理去调用了。
为什么用动态代理?
这是Retrofit设计的核心思路,基于动态代理,可以为后续在调用 GitHubService 接口的相关方法时先拦截下来,做完一系列工作后(即注解解析,请求转换,适配等),再去完成方法本尊想要完成的工作,这就是动态代理的魅力。
Tips.动态代理
Call<List<Repo>> repos = service.listRepos("octocat");
通过代理对象 service 调用接口方法 listRepos ,会被动态代理拦截,调用Proxy.newProxyInstance方法中的InvocationHandler对象的 invoke 方法。
invoke中主要由ServiceMethod和CallAdapter完成了三件事:
- 请求方法的注解解析
- 创建OkHttpCall实例,为后续流程中的HTTP请求执行做准备,详见 Flow - Invocation.
- 适配Call的响应类型,将默认响应类型R转换为类型T
|
|
ServiceMethod.java
获取callAdapter、responseType、responseConverter接口对象
解析Method的注解
解析Method的参数注解
解析Method的参数中使用了依赖请求API的动态参数的注解,交由ParameterHandler处理
CallAdapter.java
适配Call的响应类型,将默认响应类型R转换为类型T.比如官方的RxJavaCallAdapter可以结合Rxjava特性对Call的响应做RxJava观察者模式转换,进一步解放生产力。
注:未在Builder阶段指定CallAdapter(如 RxJavaCallAdapterFactory )的情况下,默认的 CallAdapter 不对Call做任何处理。
见 DefaultCallAdapterFactory:
Flow - Invocation
Response<List<Repo>> response = repos.execute();
这一步开始基于同步的方式执行HTTP请求,并得到返回的HTTP响应数据.
本质上是执行了 OkHttpCall 的 execute方法.
如你所见,这里创建了RawCall,即真正的去执行HTTP请求任务的对象。
这里还负责HTTP请求的响应数据解析。
我们看下createRawCall()
干了什么。
|
|
serviceMethod.toRequest()的功能:
toRequest 方法通过 RequestBuilder 创建了 okhttp3 做 HTTP 请求时需要的 Request 对象。
serviceMethod.callFactory.newCall(request)的功能:
建立一个请求通道,为执行HTTP请求做准备。
这里callFactory可以由使用者指定,默认为 OkHttpClient,见:
回头看下 OkHttpCall 中 execute 方法最后一句: return parseResponse(call.execute());
这里调用真正的HTTP请求客户端的请求执行方法。也就是来到了接下来的一个流程。
Flow - RawCall
上个 Flow 中最后一步, call.execute()
,开启了真正的HTTP请求,即通过 okhttp3 完成HTTP请求。
这个部分没什么代码可讲,属于面向接口开发的典范,要讲就该去讲 Okhttp 框架的源码了。
这个部分引出了 Retrofit 的开源拥有者-Square 公司的另一个优秀的开源项目 Okhttp,是不是也很想一探究竟?
End
最后我想拿 Retrofit 官方的一段话来结束:
Contributing
If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request.
When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. Please also make sure your code compiles by running mvn clean verify.
Before your code can be accepted into the project you must also sign the Individual Contributor License Agreement (CLA).
纳尼?
Cos 我想把亲手画的两副图贡献给 Retrofit.