前言
对于 Retrofit 来说网络请求本质上是 OkHttp 完成的,其仅负责网络请求接口的封装,上篇文章简单分析了 OkHttp 的源码,本篇文章来分析下 Retrofit 的源码实现,从其的简单使用方式出发。注:仓库地址
简单使用
首先需要定义一个网络请求的 API 接口,内部配合注解声明了请求方法、请求地址等信息,然后需要创建 Retrofit 实例,接着调用其 create 方法创建一个 API 接口实现类的一个实例,然后就可以通过调用该实例的指定方法拿到Call 实例,接下来就和 OkHttp 的使用方式没有什么不同了。
interface DoubanAPI { |
接下来按照 Retrofit 实例的创建、retrofit.create 方法的调用、api.inTheater 方法的调用、call.execute 方法的调用来分析 Retrofit 的源码。
Retrofit 实例的创建
这里使用了建造者模式构建 Retrofit 实例因此首先看看 Builder 的构造方法。
public static final class Builder { |
接着又调用了 baseUrl 方法用来设置当前 Retrofit 实例的 baseUrl 。
public Builder baseUrl(String baseUrl) { |
然后就直接执行 build 方法构造 Retrofit 实例。
public Retrofit build() { |
到此为止 Retrofit 实例已经创建完毕,接下来看看其 create 方法。
Retrofit.create
create 方法内部做的事情主要有注解解析,动态代理,先来看看注解解析过程。
注解解析
public <T> T create(final Class<T> service) { |
接着调用了 parseAnnotations 用于解析接口中每一个方法上的注解。
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { |
build 方法内部又分别解析了每个方法上面的注解和每个方法参数上面的注解。
解析方法上的注解
来看看 parseMethodAnnotation 这个方法内部干了些什么。
private void parseMethodAnnotation(Annotation annotation) { |
该方法解析了所有接口方法上面的注释,其中 Mutipart、FormUrlEncoded 这里处理比较简单就是两者不能共存,DELETE、GET、HEAD、PATCH、POST、PUT、OPTIONS 处理方式都一样都是直接调用了parseHttpMethodAndPath 。
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) { |
然后看看 HTTP 注解的解析过程,与 GET 相比起不同点只在于 parseHttpMethodAndPath 的方法入参都是从注解实例中取出,我们可以这么定义 HTTP 注解。
interface DoubanAPI { |
接着看看 Headers 注解的解析,首先判断了 Headers 注解的值是否设置,如果没设置就抛出错误,然后调用 parseHeaders 进行解析。
private Headers parseHeaders(String[] headers) { |
解析方法参数上的注解
RequestFactory.Builder 的 build 方法中在解析完方法上面注解后,又会调用到 parseParameter 解析方法每个参数上的注解,注意虽然每个参数可以有多个注解,但是每个参数只能拥有一个 Retrofit 注解。
// 其中p指代该参数是方法的第几个参数,parameterTypes[p]表示当前参数的参数类型, |
接着又调用到了 parseParameterAnnotation 这个方法非常长有400行左右,就不展开了只是说说其大概做了些什么. 注意里面判断了一个不常用的注解 @QueryName 这个注解表示在 Url 后面拼加一个只有 name 没有 value 的字符串。
- @Url,则需要确保以下几点,最终会创建一个 ParameterHandler.RelativeUrl 实例返回。
- 不能出现多个 @Url 注解。
- @Url 不能和 @Path 共存。
- @Url 不能声明在 @Query 后。
- @Url 不能声明在 @QueryName 后。
- @Url 不能声明在 @QueryMap 后。
- @Url 使用了就不能再在请求方法后面加上相对地址。
- 检测参数类型必须是 HttpUrl、String、URI、Uri 中的一种不然抛出错误。
- @Path,则需要确保以下几点,最终会创建一个 ParameterHandler.Path 实例返回。
- @Path 不能声明在 @Query 后面。
- @Path 不能声明在 @QueryName 后面。
- @Path 不能声明在 @QueryMap 后面。
- @Path 不能和 @Url共存。
- @Path 使用了就必须要在请求方法后面加上相对地址。
- 刚才解析方法上 Url 的时候会把所有{参数}解析出来放到 relativeUrlParamNames 这个 Set 中去,如果这个集合中不存在 Path.value 就会报错。
@Query,则会根据参数类型最终返回 ParameterHandler.Query<>(name, converter, encoded).iterable()、 ParameterHandler.Query<>(name, converter, encoded).array() 、ParameterHandler.Query<>(name, converter, encoded) 三者之一,就这告诉我们 @Query 修饰的参数类型可以是一个容器或者数组,如下所示。
interface DoubanAPI {
fun inTheaters( List<String> keywords) : Call<ResponseBody>
}
val list = ArrayList<String>()
list.add("keyword1")
list.add("keyword2")
val api = retrofit.create(DoubanAPI::class.java)
val call = api.inTheaters(list)
// 最终的url为https://api.douban.com/v2/movie/in_theaters?keyword=keyword1&keyword=keyword2@ QueryName,则会根据参数类型最终返回 ParameterHandler.QueryName<>(converter, encoded).iterable()、ParameterHandler.QueryName<>(converter, encoded).array()、 ParameterHandler.QueryName<>(converter, encoded)三者之一,跟 @Query 基本一样。
@QueryMap,首先会检查下参数类型是否是 Map,然后还会检查下 Map 的 key 是否是 String,最后会封装成 ParameterHandler.QueryMap 返回。
@Header,最终也会根据参数类型转换为 ParameterHandler.Header<>(name, converter).iterable()、 ParameterHandler.Header<>(name, converter).array()、ParameterHandler.Header<>(name, converter) 之一,这里也能看出 @Header 注解支持容器(内部类型要是 String )或者数组。
@HeaderMap,解析步骤和 @QueryMap 一样,最后返回 ParameterHandler.HeaderMap 。
@Field 首先会检查是否已经设置了 FormUrlEncoded,没设置就报错也会根据参数类型最后返回 ParameterHandler.Field<>(name, converter, encoded).iterable()、ParameterHandler.Field<>(name, converter, encoded).array()、ParameterHandler.Field 三者之一。
@FieldMap 与 @QueryMap 判断一致最后会返回一个 ParameterHandler.FieldMap 实例。
- @Part 首先检查是否设置了 Multipart 注解,没设置就报错,然后判断注解的 value 属性是否为空,接着根据类型返回 ParameterHandler.RawPart.INSTANCE.iterable()、 ParameterHandler.RawPart.INSTANCE.array()、ParameterHandler.RawPart.INSTANCE 之一,如果 value 属性不为空会先添加几个 Header,然后根据类型返回 ParameterHandler.Part<>(headers, converter).iterable()、ParameterHandler.Part<>(headers, converter).array()、ParameterHandler.Part<>(headers, converter)。
- @PartMap 与 @QueryMap判断基本一致,最终返回 ParameterHandler.PartMap。
- @Body 如果设置了@FormUrlEncoded 或者 @Multipart就报错,否则返回一个 ParameterHandler.Body 实例。
至此方法上面和请求参数上面的注解已经都解析完毕了。
HttpServiceMethod 实例的生成
接着调用 HttpServiceMethod.parseAnnotations 获取 ServiceMethod 实例将其放入到 serviceMethodCache 中。
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( |
CallAdapter 实例的获取
然后调用到了 createCallAdapter 用于获取能处理该返回类型的 CallAdapter 。
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter( |
可以看出 nextCallAdapter 会在 Retrofit 的 callAdapterFactories 里面遍历查找能处理该返回类型的CallAdapter,而 callAdapterFactories 在文章一开始已经说过了API24及以上默认包含 CompletableFutureCallAdapterFactory
、DefaultCallAdapterFactory
两个CallAdapterFactory,也就是说默认方法返回值只能是 CompletableFuture 或者 retrofit2.Call ,其余都将报错。
Converter 实例的获取
HttpServiceMethod.parseAnnotations 后面接着又会调用 createResponseConverter 获取 Convert 实例。
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter( |
跟获取 CallAdapter 流程基本一样,其会在 converterFactories 里面遍历寻找是否有 Convert 可以将ResponseBody 转化为 type 类型,API>=24会默认拥有 BuiltInConverters、OptionalConverterFactory 两个ConvertFactory,首先看看 BuiltInConverters 。
BuiltInConverters
代码如下:
// BuiltInConverters.java |
- 如果目标转化类型为 ResponseBody 并且方法上有 @Stream 注解,那么会原封不动的返回。
- 如果目标转化类型为 ResponseBody 并且方法上没有 @Stream 注解,那么会将 ResponseBody 整个读入内存(注意当文件过大可能会 OOM)。
- 如果目标转化类型为 Void,那么将 ResponseBody 直接关闭,外界拿到的响应体就是 null。
- 如果目标转化类型为 Unit,那么跟 Void 一样。
OptionalConverterFactory
代码如下:
final class OptionalConverterFactory extends Converter.Factory { |
很明显只能转化为 Optional,并且如果是 Optional 还会再检查其持有类型,至此通过 requestFactory (解析注解获取)、CallAdapter (根据方法返回值寻找)、Convert (将 ResponseBody 转化为指定类型寻找)、Call.Factory(默认设置是新建的 OkHttpClient 实例,可以传入自己的 Call.Factory )构建出 HttpServiceMethod 实例,然后将其放入 serviceMethodCache
中,到此 eagerlyValidateMethods
分析完毕,接着回到 retrofit.create 方法。
public <T> T create(final Class<T> service) { |
这里就是一个动态代理,当调用 create 方法返回实例的任何方法都会直接回调 invoke 方法。
DoubanAPI.inTheater
上述动态代理内部判断如果调用的方法来自 Object 那么直接调用,如果调用的是默认方法在 Android 中会直接抛出错误,此外就去获取 ServiceMethod 实例调用其 invoke 方法,由于我们刚才已经把所有ServiceMethod 都放入到了 serviceMethodCache 中因此直接取出执行就行了。接着看 HttpServiceMethod.invoke 。
final ReturnT invoke(Object[] args) { |
如果按照本文刚开始的例子那么 callAdapter 由 DefaultCallAdapterFactory.get 获取。
public CallAdapter<?, ?> get( |
相对于直接返回给外界了一个 OkHttpCall 实例,继续看看该类的 execute 方法。
public Response<T> execute() throws IOException { |
这个 OkHttpCall 与 RealCall 一致都只能被执行一次,先不去管响应,接着调用 createRawCall 创建 RealCall 实例。
private okhttp3.Call createRawCall() throws IOException { |
这里的 callFactoty 一般也就是一个 OkHttpClient 实例,来看看 requestFactory.create 是如何创建一个 Request 实例。
okhttp3.Request create(Object[] args) throws IOException { |
inTheaters 方法有一个参数,根据上述源码我们知道其会创建一个 ParameterHandler.Query 实例,看看其apply 方法。
// 这里的valueConverter其实是遍历了converterFactories然后调用stringConverter,如果我们没设置那么默认 |
很明显 ParameterHandler.Query 只是添加了一个请求参数,接着调用 requestBuilder.get 去真正的创建 okhttp3.Request.Builder 实例。
Request.Builder get() { |
至此创建 Request 实例成功,接着调用了 call.execute 方法执行网络请求,接下来看看其是如何解析响应的。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { |
最终调用 responseConverter 的 convert 方法将 ResponseBody 实例转化为指定的类型,由于本例中 inTheaters 方法返回值为 Call<ResponseBody>,因此这里的 T 就是 ResponseBody 类型,而 responseConverter 就是 BufferingResponseBodyConverter 实例。
static final class BufferingResponseBodyConverter |
内部直接将所有 ResponseBody 全部读入内存,然后新建一个 Response 实例返回,parseResponse 最后再调用 Response.success 构造一个成功的响应返回给外界,至此 Retrofit 的基本工作流程已经梳理完毕。下面来看看CallAdapter 和 Convert 的作用。
CallAdapter、Converter
首先看看 CallAdapter,下面是它的接口定义。
public interface CallAdapter<R, T> { |
在创建 HttpServiceMethod 的时候会遍历所有的 CallAdapter.Factory 并调用其 get 方法,一旦某个 CallAdapter.Factory 的 get 方法不返回 null 就停止遍历,如果所有的都返回 null,那么会报错。如果成功返回CallAdapter 实例 callAdapter,那么接着调用 callAdapter 的 responseType 方法获取到泛型类型,接着根据这个泛型类型去 ConvertFactory 列表中寻找可以将 ResponseBody 转化为该泛型类型的 Converter。最后在内部创建 OkHttpCall 后会调用 adapt 方法。总结下就是在 get 方法中判断 returnType 是否能处理,如果能处理的话, adapt 方法必须要将 call 转化为 returnType 类型的实例,接着看看 Converter,下面是它的定义。
public interface Converter<F, T> { |
Converter.Factory 提供了 responseBodyConverter、requestBodyConverter、stringConverter 三个重要方法,分别用于将 ResponseBody 转换为指定类型、将指定类型转换为 RequestBody、将指定类型转换为 String。三个方法都返回值都是一个 Converter 实例其只有一个方法 convert 方法内部做转换操作,下面为了加深理解来看看 GsonConverterFactory 的源码。
public final class GsonConverterFactory extends Converter.Factory { |
总结
Retrofit 的工作流程主要包括解析注解、动态代理、Request 的生成、Response 的解析四个大步骤,内部还是使用了 OkHttp 进行网络请求,其中前两步顺序可能会相反这取决于 validateEagerly 是否为 true,Request 的生成和 Response 的解析依赖于 CallAdapterFactory 和 ConverterFactory。