综述
Retrofit 并不能说是一个网络请求库,它只是一个网络请求库的封装库,在其内部网络请求是直接交给 OkHttp 完成的,与 Volley 相比,OkHttp 缺少了一些更人性化的功能,它更多地专注于如何把一个网络请求做好,所以在使用 OkHttp 进行请求的时候,我们要自己生成一个 Request,再自行处理 Response 成我们需要的数据格式。好像是为了弥补这一缺点,Square 又基于 OkHttp 给出了一个封装库,Retrofit ,Retrofit 的出现,直接将 OkHttp 的使用体验提升了几个档次,使用 Retrofit 进行网络请求,我们将不会再看到 Request 、Response ,我们声明一个方法,就代表了一个 Request ,而这个方法的返回数据,就是我们想要的 Response 。 这种方式改变了原始意义上的网络请求,用户不再关心什么 Request 和 Response ,而是这个网络请求需要什么参数,又会给出什么结果。 为了实现以调用方法替代构建 Request 的功能,Retrofit 大量使用了注解。比如,一个网络请求方法可以声明如下:
interfaceAPI{
@GET("demo/")
Call<String>getDemo(@Query("id") id);
}
如上所示,代表一个 GET 请求,请求地址为 demo/,请求参数为id=?
。而在这个例子中,使用到了两个注解,分别是 GET 和 Query ,分别代表 GET 方法和 Query 字段。Retrofit 提供的注解类型还远不止如此,足够支持绝大部分的网络请求。
流程
创建 Retrofit
Retrofit 类是使用的入口,其create()
方法用于创建一个实例,而构建 Retrofit 实例使用了 Build 模式,Build 给 Retrofit 的实例化提供参数。
构造方法参数:
okhttp3.Call.Factory,okhttp 的类,默认是 OkhttpClient ,只有一个方法,
newCall()
。HttpUrl,okhttp 中的类,表示一个 Url
List<Converters.Factory>,Converter.Factory 集,用户自定义若干 + 内建的 BuiltInConverters 。Converter 基本功能,类型转换器,Factory 提供有三种转换方式,? -> RequestBody ResponseBody -> ? ? -> String,从转换双方类型便可以看出,第一种,将任意一种类型转换为 RequestBody ,是为在发起请求的时候,将用户所给的参数类型转换为标准的请求格式,第二种就是将网络请求之后得到的标准的返回格式转换为其他任意类型,第三种,讲任意类型转换为 String,也用于在网络请求的时候转换。
List<CallAdapter.Factory>,CallAdapter.Factory 集,用户自定义若干 + 默认的,CallAdapter ,Call 适配器,可用于将 Call 类型转换为其他任意类型,API 方法默认返回是 Call<ResponseBody> 类型,通过 Converter 可以将其转换为 Call<String> Call<JsonObject> 格式,但是不能改变 Call 本身,CallAdapter 就是用于解决这个问题,它可以将 Call<ResponseBody> 转换为 Future<String> 格式。所以,在 Converter 的 ResponseBody -> ? 这一阶段,完全是可以通过 CallAdapter 直接实现的。
Executor,回调处理器,
boolean validateEagerly,是否在创建请求类实例时就加载所有的方法,默认情况是第一次调用时才会加载
加载 API 接口
创建自定义的代理类,通过重写方法调用的逻辑,一方面将原生的方法依旧调用,一方面将对应 API 的方法解析成网络请求的格式。 先将 API 方法解析成 ServiceMethod ,然后再交给 OkHttpCall 处理。
ServiceMethod
运行时通过反射解析方法,并将已经解析过的方法加入 serviceMethodCache ,从中取 ServiceMethod 的时候使用了双重检测保证线程安全。 否则就调用 ServiceMethod 的 Build 构建出一个实例。Build 完成了解析 Method 的工作,需要先得到一些 Method 相关的信息,主要的信息有:
Method 的返回类型
Method 的所有注解
Method 的所有参数,包括其数据类型和所有的全部注解 一个方法的默认返回类型应该是 Call<ResponseBody> 类型,所以如果用户不使用自定义的 CallAdapter 和 Converter ,就只能声明返回类型为这个的方法,通过自定义 Converter ,可以将 Call<ResponseBody> 转换为 Call<String>,即将 ResponseBody 转换为 Converter ,而使用自定义 CallAdapter ,则可以直接将 Call<ResponseBody> 转换为 Future<String> 。 所以,Method 返回类型的主要作用就是用于查找对应的 CallAdapter ,Retrofit 通过工厂类生成 CallAdapter ,所以需要像 Retrofit 中加入自定义的工厂类,工厂类使用
public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,Retrofit retrofit);
方法生成 CallAdapter ,Factory 通过这个方法提供的三个参数选择一个合适的 CallAdapter 返回,如 returnType 是 Future<String> ,则需要生成一个将 Call<?> 转换为 Future<String> 的 CallAdapter 。 再说 CallAdapter ,它的主要方法是T adapt(Call<R> call)
,其中 Call<R> 是原始的类型,T 就是输出的类型,以上面为例,R 就是 String ,T 是 Future<String> 。但上面说到过,方法的默认返回类型是 Call<ResponseBody> ,那为何此处输入类型却是 Call<R> ?为什么 ResponseBody 会变成一个泛型,这里则是由于 Converter 存在的原因。 上面说到过,Converter 可以将 Call<ResponseBody> 转换成 Call<String> ,那么自然也可以将其转换成任意类型,即 Call<R>,所以在 ServiceMethod.Build 的build()
方法中,先后创建了 CallAdapter 和 Converter ,而此处也说明了一个问题,那就是 Converter 会先于 CallAdapter 执行,CallAdapter 的执行依赖于 Converter 的执行,或者说,CallAdapter 的输入就是 Converter 的输出。 Converter 在两个位置执行,一是enqueue()
方法,一是excute()
方法,都是在得到原始的网络请求结果之后立刻调用的,而 CallAdapter 的adapt()
方法却是在生成调用方法之后生成 Call 的时候调用,如此看来应该是 CallAdapter 先于 Converter 工作的。从这个逻辑上讲确实,但是此刻 CallAdapter 中的 Call 却并没有得到数据,CallAdapter 最终的目的只是转换 Call ,如下一个转换为 Future 的例子:
@OverridepublicCompletableFuture<R>adapt(finalCall<R>call) {
finalCompletableFuture<R>future=newCompletableFuture<R>() {
@Overridepublicbooleancancel(booleanmayInterruptIfRunning) {
if(mayInterruptIfRunning) {
call.cancel();
}
returnsuper.cancel(mayInterruptIfRunning);
}
};
call.enqueue(newCallback<R>() {
@OverridepublicvoidonResponse(Call<R>call, Response<R>response) {
if(response.isSuccessful()) {
future.complete(response.body());
} else{
future.completeExceptionally(newHttpException(response));
}
}
@OverridepublicvoidonFailure(Call<R>call, Throwablet) {
future.completeExceptionally(t);
}
});
returnfuture;
}
Future 还是在 Call 获取数据之后再对结果进行转递,而在调用future.complete(response.body())
之时,Converter 的convert()
方法已经执行了,所以这里的 R ,就是将 ResponseBody 转换之后的结果,从这个角度而言,依旧是 Converter 先执行。
所以,CallAdapter 的数据依赖于 Converter 执行的结果,那么为了保证 CallAdapter 的输入类型与 Converter 的输出一致,CallAdapter 提供了另一个抽象方法,Type responseType()
,这里返回了一个类型,可以将其理解为 CallAdapter 需要的输入数据类型,而之后创建 Converter 实例的时候,就是根据这个类型查找 Converter.Factory ,继而生成 Converter ,二者之间的一致性就可以由此保证。以上是解析 Method 的第一步,接着会解析 Method 的注解,一般就是声明了请求方法,GET、POST 等,调用的是parseMethodAnnotation()
方法。再接着就是解析 Method 的所有参数,根据参数的数据类型和注解生成 ParameterHandler 实例,调用parseParameter()
方法。解析的逻辑是比较有意思的,对于一个参数,先判断注解类型,比如是 Quey 、Filed、Path 或 QueryMap 等,不同的类型会生成不同的 ParameterHandler ,比如 Query ,需要先根据 Query 这个注解对应的参数的类型,查找到一个 Converter ,使得能够将这个参数转换为 String 类型,因为 Query 就应该是一个 String ,如果不是,需要有 Converter 将其转成 String 。完了之后,便会生成一个 ParameterHandler 实例,ParameterHandler 是一个抽象类,它有许多的子类,而且每一个子类对应着一种注解类型,如 ParameterHandler.Query 这个子类就是用于处理 Query 这个注解的,它实现的apply()
方法,能够将参数转换成 ResquestBuilder 中的一个 Query ,如下:
@Overridevoidapply(RequestBuilderbuilder, @NullableTvalue) throwsIOException{
if(value==null) return; // Skip null values.
StringqueryValue=valueConverter.convert(value);
if(queryValue==null) return; // Skip converted but null values
builder.addQueryParam(name, queryValue, encoded);
}
另外,ParameterHandler 还有iterable()
和array()
方法,可用于处理链表、数组等数据,
finalParameterHandler<Iterable<T>>iterable() {
returnnewParameterHandler<Iterable<T>>() {
@Overridevoidapply(RequestBuilderbuilder, @NullableIterable<T>values)
throwsIOException{
if(values==null) return; // Skip null values.
for(Tvalue: values) {
ParameterHandler.this.apply(builder, value);
}
}
};
}
其本质是重写其apply()
方法,在这个方法里面先遍历 iterable ,再调用实际的apply()
方法写到 RequestBuilder 中。
当所有参数的 ParameterHandler 生成完了之后,ServiceMethod 也就算创建好了。接下来便是创建一个 Call 返回。
Call
接下来将 ServiceMethod 作为参数构建 OkHttpCall 实例,OkHttpCall 实现了 Call 接口,封装了 okhttp.Call ,okhttp.Call 的实例会在发起网络请求的时候,由 ServiceMathod 创建,SeriviceMethod 则利用存储在其内部的各种参数,如 Url、ParameterHandler 等构建出 RequestBuilder ,再由 OkHttpClient 创建出 okhttp.Call 实例返回。
OkHttpCall 实现的是 Call 接口,调用方法的最后一步,就是将 Call<ResponseBody> 由 CallAdapter 转换成方法的实际返回类型,Retrofit 有一个默认的 CallAdapter ,它的adapt()
方法的实现就是什么都不做,直接将 Call 返回。这也就解释了为什么,如果没有加入自定义 CallAdapter ,方法的返回类型就必须是 Call<?> 。
还有一点,默认的 CallAdapter 并不是什么都没做,它将 enqueue()
的 Callback 的回调切换到了主线程执行,因为在 OkHttp 中调用的enqueue()
会将请求放在线程池里执行。
由此,一个网络请求就创建完成了,下一步是执行网络请求。
执行网络请求
就以默认情况下返回的 Call<ResponseBody> 为例,Call 中声明了一些网络请求的方法,如enqueue()
、execute()
,这些方法会调用 okhttp.Call 的对应方法,比如
call.enqueue(newokhttp3.Callback() {
@OverridepublicvoidonResponse(okhttp3.Callcall, okhttp3.ResponserawRespon
Response<T>response;
try{
response=parseResponse(rawResponse);
} catch(Throwablee) {
callFailure(e);
return;
}
try{
callback.onResponse(OkHttpCall.this, response);
} catch(Throwablet) {
t.printStackTrace();
}
}
@OverridepublicvoidonFailure(okhttp3.Callcall, IOExceptione) {
callFailure(e);
}
privatevoidcallFailure(Throwablee) {
try{
callback.onFailure(OkHttpCall.this, e);
} catch(Throwablet) {
t.printStackTrace();
}
}
});
Call 有两种网络请求方式,分别对应同步和异步请求,如果是同步请求,就会直接返回一个 Response<T> ,如果是异步请求,则还需要借助回调类 Callback 完成。
返回请求结果
Retrofit 将 OkHttp 返回的数据进一步包装得到 Response<T> ,包装使用的方法就是parseResponse()
,内部逻辑为,先得到 okhttp.Response 的 ResponseBody ,再调用 Converter 将 ResponseBody 转换为其它类型。
总结
以上是 Retrofit 一个网络请求的全部流程。
在代码里,Retrofit 使用了许多设计模式,例如适配器模式(CallAdapter,Converter,ParameterHandler.iterable()),工厂模式(CallAdapter.Factory、Converter.Factory),代理模式(解析 Method 的时候,使用Proxy.newProxyInstance()
生成了一个代理实例,自定义的方法才去了自定义的执行模式),Build 模式(Retrofit.Build、ServiceMethod.Build),单例模式(BuiltInConverters.ToStringConverter),策略模式(ParameterHandler.Query…)。
除此,充分利用 Java 注解也是 Retrofit 一大特色,Retrofit 使用了一个 Method 所有可以利用的地方,方法注解、方法参数、参数注解甚至参数类型、返回类型,都被 Retrofit 加以利用,其他较多使用注解的库比如 EventBus、ButterKnife ,也有与之类似的一些效果。
从一个封装库的角度来说,Retrofit 是一个比较成功的库,用户可以通过加入自定义的 CallAdapter 和 Converter ,将网络请求方法的返回类型设置为任何所需的,并且这些并不需要用户自己写,Retrofit 有已经写好的适配器,将其加入到项目中即可使用。即便用户不想使用自定义的,光是将方法转换为 Request 的功能也值得一用了。
唯一不足的,我认为是 Retrofit 只能使用 OkHttp 作为内在的网络请求库,Retrofit 的实现也在很多地方依赖于 OkHttp ,虽然 Retrofit 可能就是作为 OkHttp 的封装库而存在的,但我觉得 Retrofit 不应止步于此。