Retrofit 核心源码分析(二)- 网络请求和响应处理

在上一篇文章中,我们详细分析了 Retrofit 中的注解解析和动态代理实现,本篇文章将继续深入研究 Retrofit 的核心源码,重点分析 Retrofit 如何进行网络请求和响应处理。

网络请求

在使用 Retrofit 发起网络请求时,我们可以通过定义一个接口并使用 Retrofit 的注解来描述这个接口中的请求,Retrofit 会自动生成一个实现该接口的代理对象。当我们调用这个代理对象的方法时,Retrofit 会根据注解的描述构建一个 Request 对象,并使用 OkHttp 将这个 Request 发送出去。

在 Retrofit 中,我们可以通过 Retrofit#executeRetrofit#enqueue 方法来发送请求。这两个方法的区别在于,execute 方法会阻塞当前线程直到请求完成,而 enqueue 方法会将请求加入到 OkHttp 的请求队列中,并在请求完成时通过回调通知我们。

我们先来看一下 execute 方法的实现:

1
2
3
4
5
public <T> T execute(Call<T> call) throws IOException {
Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());
return (T) callAdapter(call, call.request().tag()).adapt(call).execute();
}

在这个方法中,首先会对接口进行校验,确保这个接口是有效的。然后我们会根据请求的 Tag 和 URL 来获取适配器callAdapter,并使用适配器来执行请求。

适配器的作用是将请求的参数适配成 OkHttp 能够识别的形式,并将 OkHttp 的响应适配成我们需要的形式。Retrofit 提供了一系列的适配器,包括 Call 适配器、RxJava 适配器、CompletableFuture 适配器等。

我们来看一下 callAdapter 方法的实现:

1
2
3
4
5
6
private CallAdapter<?, ?> callAdapter(Call<?> call, Object tag) {
Type responseType = call.request().method().equals("HEAD")
? Void.class
: getParameterUpperBound(0, (ParameterizedType) call.request().tag());
return callAdapter(tag, responseType);
}

在这个方法中,我们首先根据请求的方法来判断响应的类型,如果是 HEAD 方法,那么响应的类型就是 Void;否则我们会通过反射来获取请求的响应类型,并使用这个响应类型来获取适配器。

获取适配器的方法是 callAdapter

1
2
3
4
5
6
7
8
9
10
public <R, T> CallAdapter<R, T> callAdapter(Object tag, Type returnType) {
// ...
for (CallAdapter.Factory factory : adapterFactories) {
CallAdapter<?, ?> adapter = factory.get(returnType, annotations, this);
if (adapter != null) {
return (CallAdapter<R, T>) adapter;
}
}
// ...
}

在这个方法中,我们会遍历所有的适配器工厂,尝试获取适配器。在获取适配器时,我们会将请求的响应类型、注解和 Retrofit 实例作为参数传入。每个适配器工厂都会判断这些参数是否符合自己的适配条件,如果符合,就返回一个适配器实例,否则返回 null。在遍历完所有的适配器工厂之后,如果还没有获取到适配器,那么就会抛出一个异常。

获取到适配器之后,我们就可以使用适配器来执行请求了。在适配器中,我们会将请求参数转换成 OkHttp 的 Request 对象,并将 OkHttp 的 Response 对象转换成我们需要的响应类型。具体的实现可以参考 Retrofit 提供的 CallAdapter 接口。

对于 enqueue 方法,我们可以先来看一下 enqueue 方法的实现:

1
2
3
4
public <T> void enqueue(Call<T> call, Callback<T> callback) {
Utils.validateServiceInterface(call.request().tag(), call.request().url().toString());
callAdapter(call, call.request().tag()).adapt(call).enqueue(new CallbackRunnable<>(callback));
}

在这个方法中,我们首先进行接口校验,然后根据请求的 Tag 和 URL 来获取适配器,并使用适配器来执行请求。不同的是,在 enqueue 方法中,我们将一个 Callback 对象作为参数传入适配器的 enqueue 方法中,以便在请求完成后回调通知我们。

在适配器中,我们可以看到 enqueue 方法的实现:

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
public void enqueue(final Callback<T> callback) {
delegate.enqueue(new Callback<Response<T>>() {
@Override public void onResponse(Call<Response<T>> call, Response<Response<T>> response) {
Response<T> body;
try {
body = response.body();
} catch (Throwable t) {
if (response.code() == 204) {
body = null;
} else {
callback.onFailure(call, t);
return;
}
}
if (response.isSuccessful()) {
callback.onResponse(call, Response.success(body, response.raw()));
} else {
callback.onFailure(call, Response.error(response.errorBody(), response.raw()));
}
}

@Override public void onFailure(Call<Response<T>> call, Throwable t) {
callback.onFailure(call, t);
}
});
}

在这个方法中,我们会将传入的 Callback 对象转换成一个 Callback<Response<T>> 对象,并使用这个对象来调用 OkHttp 的 enqueue 方法。在请求完成后,我们会将 OkHttp 的 Response 对象转换成 Retrofit 的 Response 对象,并根据响应码来判断请求的结果。如果响应码表示请求成功,那么我们就调用 Callback 对象的 onResponse 方法;否则就调用 Callback 对象的 onFailure 方法。

响应处理

在 Retrofit 中,我们可以通过定义一个接口并使用注解来描述我们期望的请求格式和响应格式。例如,我们可以通过 @GET 注解来描述一个 GET 请求,使用 @Query 注解来描述请求参数,使用 @Body 注解来描述请求体,使用 @Headers 注解来描述请求头等。

在执行请求时,Retrofit 会根据这些注解来自动生成一个对应的请求对象,并将请求对象转换成 OkHttp 的 Request 对象。在接收响应时,Retrofit 会将 OkHttp 的 Response 对象转换成一个对应的响应对象,并将响应对象中的数据转换成我们需要的数据类型。这些转换工作是通过 Retrofit 的转换器来完成的,Retrofit 中默认提供了两个转换器:GsonConverterFactoryJacksonConverterFactory。我们也可以自定义一个转换器来实现我们期望的数据转换。

在 Retrofit 类的构造方法中,我们可以看到 Retrofit 默认使用了 Platform.get() 方法来获取当前运行平台的默认转换器工厂,并将其添加到 converterFactories 中。然后,我们可以使用 addConverterFactory 方法来添加自定义的转换器工厂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Retrofit(Builder builder) {
// ...
if (builder.converterFactories == null) {
converterFactories.add(Platform.get().defaultConverterFactory());
} else {
converterFactories.addAll(builder.converterFactories);
}
// ...
}

public interface Platform {
// ...
Converter.Factory defaultConverterFactory();
}

execute方法中,我们会调用适配器的 adapt 方法来执行请求,并将返回的 Call 对象转换成一个响应对象。在转换过程中,我们会根据响应类型来选择对应的转换器来进行转换。具体的转换实现可以参考 Retrofit 提供的 Converter 接口和 Converter.Factory 接口。

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
public <T> T execute(Call<T> call) throws IOException {
// ...
Response<T> response = call.execute();
if (response.isSuccessful()) {
return response.body();
} else {
Converter<ResponseBody, ErrorResponse> converter = retrofit.responseBodyConverter(
ErrorResponse.class, new Annotation[0]);
throw new ApiException(converter.convert(response.errorBody()));
}
}

@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T adapt(Call<T> call) {
return (T) new OkHttpCall<>(requestFactory, callFactory, converter, call);
}

public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
Objects.requireNonNull(type, "type == null");
Objects.requireNonNull(annotations, "annotations == null");

int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
return (Converter<ResponseBody, T>) converter;
}
}

throw new IllegalArgumentException(
"Could not locate ResponseBody converter for " + type + " with annotations " + Arrays.toString(annotations));
}

以上是 Retrofit 中处理响应的核心代码。当我们执行一个请求时,Retrofit 会先将请求转换成 OkHttp 的 Request 对象并发送出去,然后等待响应返回。当响应返回时,Retrofit 会将响应转换成一个响应对象,并将响应对象中的数据转换成我们期望的数据类型。这个过程中,我们可以使用 Retrofit 提供的转换器来自定义数据的转换规则。

下面是一个示例,演示了如何使用 Retrofit 来发送一个 GET 请求并将响应中的 JSON 数据转换成一个 Java 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface ApiService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();

ApiService apiService = retrofit.create(ApiService.class);
Call<List<Repo>> call = apiService.listRepos("smallmarker");
List<Repo> repos = call.execute().body();

在上面的示例中,我们首先使用 Retrofit 构建器创建一个 Retrofit 实例,并指定了请求的基础 URL 和转换器工厂。然后,我们通过调用 create 方法来创建一个 ApiService 的代理对象。最后,我们调用 listRepos 方法来发送一个 GET 请求。

在上面的示例中,我们使用了 Retrofit 的 GsonConverterFactory 来将响应体中的 JSON 数据转换成 Java 对象。具体实现可以查看 Retrofit 提供的 GsonConverterFactory 类。

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
public final class GsonConverterFactory extends Converter.Factory {
private final Gson gson;

private GsonConverterFactory(Gson gson) {
this.gson = gson;
}

public static GsonConverterFactory create() {
return create(new Gson());
}

public static GsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}

@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}

@Override
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}
}

可以看到,GsonConverterFactory 继承了 Retrofit 的 Converter.Factory 类,并重写了其中的 responseBodyConverter 方法和 requestBodyConverter 方法。在 responseBodyConverter 方法中,我们将响应体中的 JSON 数据转换成 Java 对象,而在 requestBodyConverter 方法中,我们将 Java 对象转换成请求体中的 JSON 数据。

除了 GsonConverterFactory 以外,Retrofit 还提供了其他的转换器,如 JacksonConverterFactory、MoshiConverterFactory 等,我们可以根据需要选择适合自己的转换器。

总的来说,Retrofit 中网络请求和响应处理的核心代码非常简洁明了。我们只需要通过定义接口来描述请求和响应,然后使用 Retrofit 的动态代理机制来将接口转换成一个实际的实现类,并通过 Retrofit 的配置来指定请求和响应的转换器即可。这种方式大大简化了网络请求的流程,使得我们可以更加专注于业务逻辑的处理。


Retrofit 核心源码分析(二)- 网络请求和响应处理
https://smallmarker.github.io/2023/03/08/Retrofit 核心源码分析(二)-网络请求和响应处理/
作者
smallmarker
发布于
2023年3月8日
许可协议