您的位置:首页 > 其它

[置顶] Retrofit 源码分析流程

2017-07-30 18:03 405 查看

前言

Retrofit 框架使用了有一年多了,但是说来惭愧,一直都是处于使用的状态,不会用的地方利用百度或者谷歌搜索一下.一直没有去摸索里面的源代码.这几天我对源代码进行阅读之后,不仅理顺了Retrofit 框架的实现,而且对泛型的认识提升到了一个新的高度.因为里面很多代码是对泛型进行处理的.下面就容许我给大家梳理一下Retrofit 的工作原理

源码分析

首先我定义一个接口文件

public interface Api {
@GET()
Call<ResponseBody> get(@Url String uri);
}


我为了演示方便我是在Java环境下运行Retrofit的,并不是Android环境哦

public class Main {
public static void main(String[] args)
throws IOException, NoSuchMethodException {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://publicobject.com")
.build();
Api api = retrofit.create(Api.class);
retrofit2.Call<ResponseBody> call = api.get("helloworld.txt");
Response<ResponseBody> response = call.execute();
System.out.println("result = " + response.body().string());
}

}


首先是当你调用Api接口中的方法拿到Call对象的时候(之后可能是其他对象)

// 这是默认的没有加上任何CallFactory转化器和ConverterFactory和转化器的,ResponseBody对象是OkHttp提供的
retrofit2.Call<ResponseBody> call = api.get("helloworld.txt");


当你调用接口文件中的方法返回一个Call对象或者其他对象(Call对象被转化过去的)的时候执行的流程

Created with Raphaël 2.1.0Api接口文件通过动态代理ApiProxy对象InvocationHandler实现类拦截到方法method1,method2,method3通过Method对象生成ServiceMethod对象通过ServiceMethod和方法参数args对象生成OkHttpCall对象(6)获取到方法返回值(可能是OkHttpCall对象也可能是转化之后的其他对象比如Observable)结束

上面流程图的第(6)步就是Retrofit 内置的CallAdapter 转化器的作用啦.

原本默认返回的是一个Call对象,而我们可以定义一个CallAdapter转换器来将Call对象转化为其他对象,比如我们的RxJava中的ObServable

上述就是当你调用上面那句话的时候,会执行的流程.具体源码后面分析

执行请求

Response<ResponseBody> response = call.execute();
System.out.println("result = " + response.body().string());


result =
\\           //
\\  .ooo.  //
.@@@@@@@@@.
:@@@@@@@@@@@@@:
:@@. '@@@@@' .@@:
@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@

:@@ :@@@@@@@@@@@@@@@@@. @@:
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@@@@@@@@@@@@@@@
'@@@@@@@@@@@@@@@'
@@@@   @@@@
@@@@   @@@@
@@@@   @@@@
'@@'   '@@'

:@@@.
.@@@@@@@:   +@@       `@@      @@`   @@     @@
.@@@@'@@@@:  +@@       `@@      @@`   @@     @@
@@@     @@@  +@@       `@@      @@`   @@     @@
.@@       @@: +@@   @@@ `@@      @@` @@@@@@ @@@@@@  @@;@@@@@
@@@       @@@ +@@  @@@  `@@      @@` @@@@@@ @@@@@@  @@@@@@@@@
@@@       @@@ +@@ @@@   `@@@@@@@@@@`   @@     @@    @@@   :@@
@@@       @@@ +@@@@@    `@@@@@@@@@@`   @@     @@    @@#    @@+
@@@       @@@ +@@@@@+   `@@      @@`   @@     @@    @@:    @@#
@@:     .@@` +@@@+@@   `@@      @@`   @@     @@    @@#    @@+
@@@.   .@@@  +@@  @@@  `@@      @@`   @@     @@    @@@   ,@@
@@@@@@@@@   +@@   @@@ `@@      @@`   @@@@   @@@@  @@@@#@@@@
@@@@@@@    +@@   #@@ `@@      @@`   @@@@:  @@@@: @@'@@@@@
@@:
@@:
@@:


那么这时候我们我们调用的代码执行的流程是怎么样的呢?

我们上面说过调用接口的方法返回的是一个Call对象或者是其他一个转化过后的对象,我们由于还没有添加转化器,所以默认就只能返回Call对象。

同时我们上面的流程图也介绍了默认返回的Call(其实就是OkHttpCall,实现了Retrofit中的Call接口)其实就是对OkHttp中的Call对象中的一个包裹(也可以说是一个代理)

所以当你执行下面的代码

Response<ResponseBody> response = call.execute();


其实就会调用到OkHttp中的Call对象的 execute() 方法,下面我画一下流程图

Created with Raphaël 2.1.0调用call.execute()或者enqueue(Callback<T> callback)方法实际上调用OkHttp中的Call对象的execute() 或者 enqueue(Callback<T> callback)方法请求服务器返回数据,对数据进行包装成一个ResponseBody 对象(4)利用Retrofit中的Converter转化器将ResponseBody对象转化为其他对象结束

上面的(4)至关重要,这里是对数据就行转换的关键.我们使用过Retrofit的同学都知道,我们的Retrofit可以通过添加转化器对返回的json、xml等数据都可以转化成想要的数据.比如常见的json转化成为实体对象供我们使用

流程总结

1.对于Retrofit 有两个流程,就是上述的两个.第一个流程是获取一个可请求的对象.这时候请求并没有发送,你获取到的只是一个OkHttp中的Call对象的一个代理对象.这个对象很可能是一个多次代理的对象,其中内部的OkHttpCall就是对OkHttp中的Call对象的第一层代理.如果你使用了RxJava,你返回的ObServable 对象就是对OkHttpCall的二次代理.

而 OkHttp中的Call 是一个可请求的对象,内部封装并且持有了一个Request

2.第二个流程就是你拿到Retrofit的默认对OkHttpCall代理的Call对象(和OkHttp的Call是两个类,不要混淆) 或者经过转化的其他对象,比如ObServable

如果是默认的Call的话,调用enqueue方法或者execute方法即可请求网络拿到数据.

如果是ObServable 只需要订阅即可,就可以拿到数据

阅读源码证明上述流程

public static void main(String[] args) throws IOException, NoSuchMethodException {

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://publicobject.com")
.build();

Api api = retrofit.create(Api.class);
Call<ResponseBody> call = api.getCall("helloworld.txt");
Response<ResponseBody> response = call.execute(); System.out.println("result = " + response.body().string());

}


首先上面的代码是成功请求的代码

流程1

我们可以看到首先构建了一个Retrofit,然后

Api api = retrofit.create(Api.class);


这句代码发生了什么呢?

代理接口



有Java动态代理知识的同学可以很明显发现这里使用了动态代理.对接口Api进行了一个代理.

而匿名内部类实现了InvocationHandler 接口就是拦截到所有的方法

ServiceMethod对象的创建

然后我们往下看,我们发现通过方法loadServiceMethod(method)创建了ServiceMethod对象,ServiceMethod对象内部根据method中解析了注解信息和返回值类型,并且根据参数中的注解为每一个参数生成一个处理对象,构建ServiceMethod是采用了建造者模式

ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;

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


public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
responseConverter = createResponseConverter();

for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}

if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}

if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}

Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}

parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}

if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}

return new ServiceMethod<>(this);
}


loadServiceMethod(method) 这个方法内部对获取ServiceMethod进行了缓存,这样子我们调用同一个方法的时候就不会多次解析method中的注解信息,浪费时间

后面的build方法是构建出ServiceMethod对象的Builder对象的build方法。可以看到里面对方法上的注解信息的解析和各种健壮性的判断

OkHttpCall(其实就是Retrofit中的Call接口的实现类) 对象的创建



创建OkHttpCall对象我们传入了ServiceMethod对象和实际调用方法的时候的参数数组.

上面说过ServiceMethod解析了方法上的注解信息和生成了各个参数的处理类

(每一个处理类还会遍历所有的转化器,找到第一个支持解析接口中方法参数类型的Converter)

还包括找出了可以转化Api接口中写的泛型中的对象的转化器Converter

现在有ServiceMethod 和 调用方法的参数args 那么就可以生成一个真正的请求对象了.

ServiceMethod 中的信息 + ServiceMethod中的参数处理类处理args得到的信息 = Request对象

OkHttpCall 中有一段代码

private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}


从这里可以看到调用了ServiceMethod 对象的toRequest方法生成了一个Request对象,用过OkHttp的同学就很清楚了,这里的Request对象就是OkHttp的,然后接下来对这个Request包裹了一下,变成了 okhttp中的Call对象.而 这个call是OkHttpCall对象持有的,所以之前说OkHttpCall是OkHttp中的Call对象的一个代理类,这里得到了验证.

然后下面是转化Call的代码



这里的代码看方法名字其实很清楚,就是对OkHttp做转换。里面会遍历所有的CallAdapter,如果某一个CallAdapter 可以支持生成你在接口文件中写的类型,那么就获取到这个CallAdapter 让他对OkHttpCall 对象做转化,这也就是下面代码能返回ObServable 对象的原因

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://publicobject.com")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

Api api = retrofit.create(Api.class);
Observable<ResponseBody> observable = api.get("helloworld.txt");


public interface Api {

@GET()
Observable<ResponseBody> get(@Url String uri);

}


流程2

这时候参数也解析了.请求也有了,就差流程2调用啦,流程2的代码就不一一展示了,但是展示一个最关键的返回的数据类型的转化的部分

上面陈述了OkHttpCall 是对OkHttp中的Call对象的一个代理,所以OkHttpCall内部执行请求之后就是拿到返回值最近的地方,让我们看看,我们以同步的方法为例子:

public Response<T> execute() throws IOException {
okhttp3.Call call;

synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;

if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else {
throw (RuntimeException) creationFailure;
}
}

call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}

if (canceled) {
call.cancel();
}

return parseResponse(call.execute());
}

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();

// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();

int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}

if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}

ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}


ServiceMethod 对象中的方法

/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}


可以看到最后的代码中,又通过 ServiceMethod 对象的toResponse 方法生成一个Response对象,内部利用 ServiceMethod 持有的类型转化器 Converter 对ResponseBody 进行了转化,比如你添加了Gson的转化器,就会帮你转化成对象.不添加任何一个转化器的时候,默认返回的是一个ResponseBody 对象。这在最开始就展示了.

总结

好处:

Retrofit 提供了很高的扩展性,让我们可以定制返回的对象和返回的结果.这可以和轻易的和其他框架搭配使用,影响力比较大的就是和RxJava搭配使用.

同时你还可以自定义转化器 Converter,让你的接口方法中的参数支持更多的参数类型,比如File,Map,List等等,虽然一些集合内部已经支持了.但我就是举个例子.嘿嘿嘿

不足之处:

内部就是基于OkHttp 实现的,并不能让Retrofit 支持其他的网络框架,比如 HttpUrlConnection

如果非要使用Retrofit但是内部使用的是其他的网络框架的话,只能使用 内部的 OkHttp 的拦截器,让所有的方法都走另一个网络框架的请求,也是可以的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: