Retrofit2 代理请求解析

Retrofit2 代理请求解析

前言

Retrofit 是一个网络请求框架,它可以将 HTTP API 转换成 Java 中的接口,通过其灵活的设计模式将底层的网络请求和上传的业务封装结合起来,使网络调用变得十分简单

这篇文章的目的就是深入解读 Retrofit2 是如何采用各种各样的设计模式来完成仅仅通过接口的定义来完成复杂的网络请求

基本使用

Retrofit 将 HTTP API 转化为 Java 接口

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

Retrofit 类去生成一个 GitHubService 接口的实现

1
2
3
4
5
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();

GitHubService service = retrofit.create(GitHubService.class);

GitHubService 实现中的每个 Call 都能实现同步或则异步的HTTP 请求

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

流程解析

流程的关键在于 Retrofit 是如何处理 API 接口的实现,我们可以通过 Retrofit.create 方法去深入探究

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
26
27
28
29
30
31
32
33
public <T> T create(final Class<T> service) {
// 接口校验,service 必须是 interfaces
Utils.validateServiceInterface(service);
// 是否对方法进行预加载
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 关键代码,通过 Java 动态加载机制来实现对接口方法的动态代理
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();

@Override
public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// 如果是来自普通对象则使用默认反射调用,其实已经在前面做过预判了
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 对平台额外处理
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// ServiceMethod 存储了一些请求相关参数,通过 loadServiceMethod 反射获取方法注解得到
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
// Retrofit 底层网络请求由 okhttp 实现
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 最终返回的是有 CallAdapter 封装的数据结构
return serviceMethod.adapt(okHttpCall);
}
});
}

Retrfit 采用了动态代理模式,当调用接口的方法时,会动态地由 InvocationHandler.invoke 去实现具体的逻辑,而 Retrofit 里面最主要的是构建 ServiceMethod 对象的工作,对于请求的一些参数,封装都是由这个对象持有的,而构建过程则是在 Retrfit.loadServiceMethod 方法中

Retrfit.loadServiceMethod 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ServiceMethod<?, ?> loadServiceMethod(Method method) {
// 因为反射的方法在 Android 中效率较低,所以设有缓存,可以避免多次加载
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
//做了同步控制
synchronized (serviceMethodCache) {
// 再次判断防止并发
result = serviceMethodCache.get(method);
if (result == null) {
// ServiceMethod 构建采用的建造者模式
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}

ServiceMethod 的构造过程需要两个参数,一个是 Retrofit 对象,用来获取用户构造 Retrofit 对象时传入的一些配置,另一个就是 Method 对象,用来获取具体的接口配置参数,最终在 ServiceMethod.Builder.build 方法中创建 ServiceMethod 对象

ServiceMethod.Builder.build 方法

该方法比较长,所以会选择性略去一些代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public ServiceMethod build() {
// 获取 callAdapter 和 返回值类型
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();

... 省略一些校验

// 获取 responseConverter
responseConverter = createResponseConverter();

// 处理方法注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}

... 省略一些校验

return new ServiceMethod<>(this);
}

关于 callAdapter 和 responseConverter 后面会讲到,这写先跟踪 parseMethodAnnotation 方法去找到如何将注解转化到请求参数里

ServiceMethod.Builder.parseMethodAnnotation 方法

1
2
3
4
5
6
7
8
9
10
11
12
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} else if ... 省略
}

每个注解类型都有对应的解析方法,深入到 parseHttpMethodAndPath 方法里可以得知 ServiceMethod 从注解中获取了 httpMethod,hasBody 属性,还通过正则去获取了 relativeUrl,relativeUrlParamNames(这个是一个 Set 路径填充参数,会与请求参数做匹配动态生成目标URL)

在得到了 ServiceMethod 得到所有请求相关参数后,回到 Retrofit.create 方法那边,会封装出一个 new OkHttpCall<>(serviceMethod, args),而真正的 okhttp 请求对象则是在 OkHttpCall.createRawCall 方法里生成的,这个方法中又调用了传入的 ServiceMethod 对象的 toCall 方法,所以请求的生成也是交由 ServiceMethod 控制的,无非是将传入的参数与解析出来的请求参数合并生成一个 okhttp 请求体 okhttp3.Call,最后再通过 ServiceMethod 中的 callAdapter 进行流程封装,真正触发网络请求

CallAdapter & Converter

CallAdapter 是 Retrofit 的网络执行器,负责创建 okhttp 的网络请求对象,发送网络请求,而 Converter 则是 Retrofit 的数据解析器,负责数据转换解析。

这两者都是通过策略模式实现的,我们可以通过官方提供的 adapter-rxjava2 以及 converter-gson 两种实现来使用 rxjava2 来调用 Retrofit 的接口请求以及通过 gson 将接口的 json 数据转化为对象。而对应实例生成也用到抽象工厂模式。在 Retrofit 中定义了 CallAdapter 和 Converter 接口以及抽象工厂类。

我们通过 adapter-rxjava2 和 converter-gson 两个包的引入来具体分析 Retrofit 的网络请求流程。

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

不涉及到具体的 CallAdapterFactory 时,网络请求通过 ServiceMethod.adapt 方法,传入 OkHttpCall 对象,最终
通过 ServiceMethod 中持有的 CallAdapter 的 adapt 方法执行

ServiceMethod 中持有的 CallAdapter(Retrofit 中传入工厂类,最终会根据 returnType 得到真正的 CallAdapter)进行真正的网络请求,在 RxJava2CallAdapterFactory.get 方法中,我们得到真正的 RxJava2CallAdapter 类)继续追踪 adapt 方法

RxJava2CallAdapter.adapt 方法

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
@Override 
public Object adapt(Call<R> call) {
// 根据是否同步创建对应的 Observable 类型
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
// 额外配置处理
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
// 对请求线程的处理
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
// 对 Rxjava2 返回类型的处理
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return observable;
}

生成一个 observable,根据设定进行额外的处理,最终返回我们想要的数据类型,这里我们以同步请求 CallExecuteObservable 为例继续追踪

CallExecuteObservable 类
代码不多,全贴出来

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
final class CallExecuteObservable<T> extends Observable<Response<T>> {
private final Call<T> originalCall;

CallExecuteObservable(Call<T> originalCall) {
// 这里的 originalCall 就是上面提到的 OkhttpCall 实例
this.originalCall = originalCall;
}

// 由 RxJava2 流程可知,订阅事件会追溯到这个方法执行
@Override
protected void subscribeActual(Observer<? super Response<T>> observer) {
// Call 由于 okhttp 的限制只能执行一次,类似 Runnable,所以在这里克隆一个对象去执行
Call<T> call = originalCall.clone();
CallDisposable disposable = new CallDisposable(call);
observer.onSubscribe(disposable);

boolean terminated = false;
try {
// 通过 http 真正的执行网络请求,值得注意的是这里已经将数据转换完成了
Response<T> response = call.execute();
if (!disposable.isDisposed()) {
observer.onNext(response);
}
if (!disposable.isDisposed()) {
terminated = true;
observer.onComplete();
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (terminated) {
RxJavaPlugins.onError(t);
} else if (!disposable.isDisposed()) {
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
}
}

// 用于取消订阅
private static final class CallDisposable implements Disposable {
private final Call<?> call;
private volatile boolean disposed;

CallDisposable(Call<?> call) {
this.call = call;
}

@Override
public void dispose() {
disposed = true;
call.cancel();
}

@Override
public boolean isDisposed() {
return disposed;
}
}
}

而 Converter 是何时发生作用的呢,在 Response response = call.execute() 时就已经返回对应的数据结构了,所以我们需要追溯到 OkhttpCall.execute 方法里去

OkhttpCall.execute 方法

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
@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 if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
// 由 createRawCall 方法创建真正的 okhttp 的 Call
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}

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

// 得到返回后会对数据解析
return parseResponse(call.execute());
}

OkhttpCall.parseResponse 方法

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
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 {
// 数据转换工作还是交由 serviceMethod.toResponse 执行
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.toResponse 方法

1
2
3
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}

这里的 responseConverter 通过 Build.createResponseConverter 创建,通过 responseType 从工厂类通过 responseBodyConverter 获取

而 Converter.convert 方法具体实现交由 GsonResponseBodyConverter 实现,最终通过 gson 相关方式转化

其实除去具体的 CallAdapter 和 Converter 逻辑实现,以图画表现更清晰

结语

Retrofit 通过 okhttp 执行网络请求,通过 CallAdapter 和 Converter 包装上层数据返回,实现了一个完美的中间层封装,用到了动态代理,抽象工厂,策略等模式,结构清晰,代码解耦,是一个非常值得学习和借鉴的开源库。

接下去还有网络请求的真正执行者 okhttp 的代码解析了