0%

Retrofit2源码分析

1. 前言

根据Retrofit官网的定义:A type-safe HTTP client for Android and Java,意即Retrofit是一个类型安全的HTTP网络请求框架。本文先介绍Retrofit2的基本使用方法,再逐步分析Retrofit代码基本实现,最后对Retrofit优缺点进行总结。

2. 基本使用

使用非常简单,可以参考Retrofit官网

2.1 定义接口

1
2
3
4
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}

2.2 构建实例

1
2
3
4
5
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);

2.3 转换

1
2
Call<List<Repo>> call = service.listRepos("octocat");
List<Repo> repos = call.execute().body();

3. 代码分析

Retrofit严格意义上来说,不算一个网络请求框架,因为Retrofit2的主要工作是把请求用清晰、简洁的方式描述出来,并把一个HTTP API转换成Java接口。具体的网络请求工作其实是由OkHttp来完成的。下面我们从请求描述和请求发起和网络请求管理三个方面来分析。

3.1 如何描述请求

我们关心:Retrofit是怎么把Java接口转成一个Call的,又是怎么把一个Call转成一个可以发送请求的Request的。

对于定义的接口,使用动态代理,每一个接口方法调用时,都会调用==invoke==方法

3.1.1 Retrofit.create
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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() {
// 针对不同平台做适配(Java/Android)
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

此处使用了缓存,对于加载过的方法,直接从cache中拿。ServiceMethod主要作用是把一个Java接口映射到Http请求上.泛型R表示返回请求值,T表示Java接口返回值(Demo中Call>)

问题:为何有了Converter还要有CallAdapter? 二者有和区别?

Converter作用主要是提供不同数据类型的转换,比如把ResponseBody转成List,把RequestBody数据转换成Json格式,等等。抽象成一个接口便于扩展,增加灵活性;

CallAdapter主要作用是把Call>转成Java对象(List)

3.1.2 loadServiceMethod
1
2
3
4
5
6
7
8
9
10
11
12
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;
}

每一个Retrofit2中Call(此处拿OkHttpCall举例,不一定是Call,如果是RxJava,则为Obserable,原理一致)调用execute()方法之后,返回的是一个Retrofit2的Response。有个比较有趣的地方是Retrofit2中的OkHttpCall不是真正发起请求的,而是调用OkHttp3.Call。而Retrofit2中的Response也不是真相的返回结果,而是利用OkHttp3.Response作为返回。这种组合的方式有个好处就是解耦,以后若不想用OkHttp3改用其他框架,工作量也会小很多。

上面已经把Java接口转成Call部分说完了,接下来看如何把Call转成Request.

3.1.3 ServiceMethod.Builder.build()

在build()方法中主要是解析方法注解、方法参数注解,从而得到一个Http请求的完整信息(如httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static final class Builder<T, R> {
// 省略部分代码...

public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
// 省略部分代码...
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
// 解析方法对应注解
parseMethodAnnotation(annotation);
}
// 省略部分代码...
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
// 省略部分代码...

// 获取参数对应的注解
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
// 省略部分代码...

// 解析参数对应注解
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
// 省略部分代码...
return new ServiceMethod<>(this);
}
3.1.4 ServiceMethod.toRequest()

之后,再通过toRequest()把步骤3.1.4中的信息(httpMethod, baseUrl, relativeUrl, headers,contentType, hasBody, isFormEncoded, isMultipart)保存到==OkHttp3.Request==中。具体参数替换和赋值(如替换Url、修改relativeUrl)等操作则放在ParameterHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** Builds an HTTP request from method arguments. */
Request toRequest(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}

至此,把Call转换成一个OkHttp3.Request类的过程分析完毕。

3.2 发起请求

实际上发送请求的是OkHttp3而不是Retrofit。因此发送请求过程十分简单,使用一个包装类OkHttpCall包装OkHttp3.Call请求,返回结果。

3.2.1 Retrfit.OkHttpCall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Override 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 {
// 使用OkHttp3.Call进行网络请求,并解析结果
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
// 解析结果
return parseResponse(call.execute());
}
3.2.2 OkHttpCall.createRawCall && parseResponse()

使用createRawCall创建OkHttp3.Call请求,并通过parseResponse()解析返回结果并包装成Retrofit.Response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//新建网络请求
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;
}
// 解析网络请求结果
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;
}
}

至此,一个接口从发起到返回结果,并包装成Java对象的过程分析完毕。

3.2.3 大致流程
  1. 创建Retrofit实例 (retrofit),设置一些全局参数如baseUrl、converer等
  2. 创建Java接口实例 (service)
  3. 调用Java接口实例具体方法 (service.listRepos(“octocat”)),生成ServiceMethod实例,用来管理接口
  4. 由ServiceMethod解析请求参数
  5. 由代理Call发出的请求放到OkHttp3请求队列中请求
  6. 将请求结果回调
  7. 最终将数据转化成目标类型

3.3 请求管理

Retrofit2的请求其实是借用的OkHttp3的网络请求。这部分内容将在下篇文章进行介绍。

总结

Retrofit2写得非常短小精悍,其巧妙地利用动态代理、泛型、缓存等技术,引入了Builder模式、适配器模式等设计模式,为Retrofit扩展性提供了强大的保证,代码可读性高的同时也不失效率。就网络框架上,个人可能更倾向于使用Volley,但是Retrofit2使用注解来描述请求的这个思想包括使用到的技术都可以借鉴,扩展开来,数据库或许是另一个使用注解描述一个Query的场景。