前言
众所周知🤪,Retrofit内部封装了OkHttp的实现。准确的说,Retrofit在OkHttp的基础上进行了上层的扩展,有效地解决了OkHttp代码逻辑调用不怎么优雅的问题,同时为开发者提供更方便的功能模块。Retrofit的官方文档中有给出一个简单的网络请求示例。本文将根据示例,分析Retrofit的源码。
本文使用的Retrofit版本为2.9.0
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
流程概要
文档给出的示例代码:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
// 1. 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
// 2. 实例一个GitHubService对象
GitHubService service = retrofit.create(GitHubService.class);
// 3. 发起网络请求
Call<List<Repo>> repos = service.listRepos("octocat");
call.enqueue(new Callback<List<Object>>() {
@Override
public void onResponse(@NonNull Call<List<Repo>> call, @NonNull Response<List<Repo>> response) {
}
@Override
public void onFailure(@NonNull Call<List<Repo>> call, @NonNull Throwable t) {
}
});
使用Retrofit进行网络请求首先需要定义一个interface,方法即为网络请求相关设置的抽象。接下来就是三步曲了:
- 创建
Retrofit实例,利用Retrofit.Builder()构建一个Retrofit对象,在这个过程中会设置一些参数。比如:- 服务端的baseUrl。
- 若需要自定义一些
OkHttp的拦截器的,开发者需要自定义一个OkHttpClient。 Retrofit支持请求和响应的类型转换的自定义(Converter),以及适配其他库(如RxJava)的自定义(Adapter)。这个会放在后续文章讲解。 这一过程一般会放在应用初始化,Retrofit实例生命周期也会与app一致。避免重复创建的开销。
- 实例一个
GitHubService对象,利用Retrofit实例构建一个上述定义的interface的对象(ps:其实是Retrofit帮我们虚构的一个对象,后面会讲到)。 - 发起网络请求,首先会调用具体的方法
service.listRepos("octocat"),即表示我们需要请求"users/{user}/repos"这个接口。接下来的调用与OkHttp类似,但值得注意的是这里的Call是Retrofit自身定义的。
以上就是Retrofit的简单使用流程,接下来会一一分析三部曲的源码。在此之前引用一张有关Retrofit的流程图,涵盖了整个运作流程:
创建Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
在创建时,涉及到了外观模式、builder模式的思想。其实就是作为Retrofit的一些全局配置逻辑。这里重点关注build()方法
// Retrofit.java
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 1. OkHttpClient没有自定义时,使用默认的
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 2. 请求回调时需要使用一个特定的线程池作为回调
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// 3. 执行过程中的适配器(Adapter)构建工厂列表
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// 4. 请求/响应的数据转换器(Converter)构建工厂列表
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
build()方法为Retrofit对象的构造提供必要参数。baseUrl比较好理解,这里就不展开了。
callFactory
callFactory即为OkHttp中的OkHttpClient,如果开发者没有设置的话,这里会默认新建一个,以供后续的网络请求。设置是在Retrofit.Builder中的:
// Retrofit.java
public Builder client(OkHttpClient client) {
return callFactory(Objects.requireNonNull(client, "client == null"));
}
callbackExecutor
callbackExecutor控制网络请求响应后需要回调的线程。这也是Retrofit之于OkHttp的一次封装,可以为开发者做一次线程转换。这里会有一个默认的callbackExecutor,是根据平台区分创建的。此处运用了策略模式的思想。
// Retrofit.java
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
// Retrofit.Builder#build()
platform.defaultCallbackExecutor();
// Platform.java
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
// 根据jvm区分当前平台为Android还是Java,且默认支持Java8
private static Platform findPlatform() {
return "Dalvik".equals(System.getProperty("java.vm.name"))
? new Android()
: new Platform(true);
}
默认的Plantform对象是在Retrofit.Builder的构造方法获取的,findPlatform()方法中会根据jvm区分平台。由于本文偏向Android,所以暂选择Android类作为解析对象。
拓展一个冷知识,在旧版本Retrofit(譬如2.4.0),平台还支持iOS。是利用RoboVM环境使用Java开发iOS的技术。有兴趣可以参考: RoboVM:使用Java开发iOS应用
// Platform.java
static final class Android extends Platform {
Android() {
super(Build.VERSION.SDK_INT >= 24);
}
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Nullable
@Override
Object invokeDefaultMethod(
Method method, Class<?> declaringClass, Object object, Object... args) throws Throwable {
if (Build.VERSION.SDK_INT < 26) {
throw new UnsupportedOperationException(
"Calling default methods on API 24 and 25 is not supported");
}
return super.invokeDefaultMethod(method, declaringClass, object, args);
}
static final class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
}
Android#defaultCallbackExecutor返回的是一个MainThreadExecutor,内部持有一个主线程的Handler。它的execute方法即为将一个Runnable发送到主线程的MessageQueue。达到子线程切换到主线程的目的。
callAdapterFactories
callAdapter是Retrofit抽象出来的关于okhttp3.Call执行网络请求的封装生成类。详细的介绍放到下文。
// Retrofit.java
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
// Platform.java
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
- 开发者可以根据自身逻辑在
Builder的时候通过addConverterFactory添加相关的CallAdapter的工厂类。譬如后续文章会介绍的RxJavaCallAdapter。 Platform类中默认会实现一个defaultCallAdapterFactories方法,用于获取Retrofit自带的。CallAdapter.Factory,这里会根据平台是否适配Java8添加一个CompletableFutureCallAdapterFactory以及默认的DefaultCallAdapterFactory。本文着重介绍DefaultCallAdapterFactory。
converterFactories
Converter转换器,Retrofit在请求和响应时,可以根据Converter作一层数据转换,将上层传递下来的对象转换为网络请求通用的对象,或将响应回来的数据解析成开发者所需要的数据。
// Retrofit.java
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
// Platform.java
List<? extends Converter.Factory> defaultConverterFactories() {
return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList();
}
- 默认会添加一个
BuiltInConverters,支持OkHttp的RequestBody、ResponseBody的转换。 - 开发者可以根据自身的需要添加自定义的
Converter。 Platform会根据是否支持Java8添加OptionalConverterFactory,这个在本文不作介绍。
小结
至此,build()方法介绍完成,方法中涵盖了网络请求的核心OkHttpClient、回调的线程设置、请求过程中的适配器以及请求和响应的数据转换器。
Retrofit#create
GitHubService service = retrofit.create(GitHubService.class);
// Retrofit.java
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable 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);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
在定义网络接口层时,我们指定义了一个interface GitHubService,刚开始笔者以为会是像Room之类的库一样采用编译时生成一个Impl类的做法。但Retrofit#create方法可以说是巧妙地运用了动态代理了。采用动态代理,在调用具体接口方法时,会触发上述代码的invoke方法,再通过Method对象获取具体的参数及方法、参数的注解,从而转换成OkHttp需要的参数格式。
发起网络请求
发起网络请求根据上述的示例代码可分为两步,1、调用具体的网络接口层,2、发起请求
// 1
Call<List<Repo>> repos = service.listRepos("octocat");
// 2
call.enqueue(new Callback<List<Object>>() {
@Override
public void onResponse(@NonNull Call<List<Repo>> call, @NonNull Response<List<Repo>> response) {
}
@Override
public void onFailure(@NonNull Call<List<Repo>> call, @NonNull Throwable t) {
}
});
调用具体的网络接口层
Call<List<Repo>> repos = service.listRepos("octocat");
在Retrofit#create中提到,调用具体的方法时实际上是调用了InvocationHandler#invoke
// Retrofit.java
@Override
public @Nullable 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);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
这里会判断是否为Java8的特性interface的默认方法,此处我们只关注loadServiceMethod(method).invoke(args);这一情况。
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
Retrofit#loadServiceMethod
Retrofit#loadServiceMethod内逻辑比较简单,可以理解为是
- 通过
ServiceMethod类解析并保存有关method的信息,如:方法注解、参数注解、参数等。 - 通过
serviceMethodCache,一个Map形成全局缓存,优化二次请求时重复解析的开销。
ServiceMethod.parseAnnotations即解析method的信息。
ps:ServiceMethod在低版本是一个实现类,高版本可能是由于需要适配Kotlin的协程所以将ServiceMethod改成抽象类。具体依赖关系:
classDiagram
ServiceMethod <|-- HttpServiceMethod
HttpServiceMethod <|-- CallAdapted
HttpServiceMethod <|-- SuspendForBody
ServiceMethod.parseAnnotations最终调用的是HttpServiceMethod.parseAnnotations,本文不关注有关Kotlin协程的逻辑。下述代码先忽略掉。
// ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 解析method的信息,以供后续的网络请求数据组装。
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
// 一些返回类型的校验...
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
// HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
// ...
} else {
adapterType = method.getGenericReturnType();
}
// 通过方法返回类型adapterType和方法注解annotations寻找合适的callAdapter
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
// 根据方法返回类型responseType寻找合适的converter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
// 创建对应OkHttp封装的ServiceMethod对象
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
// ...
} else {
// ...
}
}
- 首先关注
ServiceMethod.parseAnnotations中的RequestFactory.parseAnnotations(retrofit, method);主要逻辑是存储method对象中有关请求的参数,并根据参数组装对应的converter用于请求时的数据组装,以及对于注解使用的校验。这里就不过多介绍了。 - 调用
HttpServiceMethod.parseAnnotations- 寻找合适的callAdapter
- 寻找合适的converter
- 根据以上信息创建一个
ServiceMethod对象(CallAdapted)
获取callAdapter
// HttpServiceMethod.java
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
// Retrofit.java
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(
@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
Objects.requireNonNull(returnType, "returnType == null");
Objects.requireNonNull(annotations, "annotations == null");
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
// 抛异常逻辑...
}
前面讲Retrofit.Builder#build时有提到,Retrofit会持有创建callAdapter的工厂类CallAdapter.Factory的列表,即callAdapterFactories。nextCallAdapter方法就是找出能够根据returnType、annotations的信息创建出合适的callAdapter。以DefaultCallAdapterFactory为例:
// DefaultCallAdapterFactory.java
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
DefaultCallAdapterFactory#get首先校验的就是该方法的返回类型是否为Call类型。对应的是:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
留意一下这里返回了一个CallAdapter的匿名类对象,adapt方法是后续流程会调用到的,这个放到后面讲解。
获取converter
获取converter与获取callAdapter的逻辑类似,只是这次是找出合适的Converter.Factory,并创建一个reponseBodyConverter:
// HttpServiceMethod.java
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}
// Retrofit.java
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) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
// 抛异常逻辑...
}
需要注意的是,Retrofit默认添加的BuiltInConverters其实是无法转换成自定义对象的,可以参考以下代码
@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == ResponseBody.class) {
return Utils.isAnnotationPresent(annotations, Streaming.class)
? StreamingResponseBodyConverter.INSTANCE
: BufferingResponseBodyConverter.INSTANCE;
}
if (type == Void.class) {
return VoidResponseBodyConverter.INSTANCE;
}
if (checkForKotlinUnit) {
try {
if (type == Unit.class) {
return UnitResponseBodyConverter.INSTANCE;
}
} catch (NoClassDefFoundError ignored) {
checkForKotlinUnit = false;
}
}
return null;
}
即我们按照示例代码创建的Retrofit对象其实并没有将响应数据转换成自定义对象的能力,所以常规的是添加一个GsonConverterFactory,代码会修改成这样:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
ServiceMethod#invoke
回到InvocationHandler#invoke中,执行完loadServiceMethod后会调用ServiceMethod#invoke。
// ServiceMethod.java
abstract @Nullable T invoke(Object[] args);
// HttpServiceMethod.java
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
// CallAdapted.java
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
由于ServiceMethod#invoke是一个抽象方法,由HttpServiceMethod实现。这里invoke在HttpServiceMethod中被声明为了final(ps:HttpServiceMethod也是一个抽象类)。HttpServiceMethod#invoke方法最终会调用adapt,adapt由子类实现。
HttpServiceMethod#invoke中新建了一个OkHttpCall对象,该对象继承自Call接口(没错,和上面看到那个Call是同一个,这个是具体的实现类)。该类封装了OkHttp的网络请求逻辑,是真实执行网络请求的地方。CallAdapted#adapt中callAdapter即为上一节所提到的由DefaultCallAdapterFactory#get返回的匿名CallAdapter对象。调用其adapt方法,会返回一个ExecutorCallbackCall对象,也是继承自Call接口。这里采用了装饰器的思想。
// DefaultCallAdapterFactory.java
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
所以回到应用层,返回的Call对象在这种情况下实际是一个ExecutorCallbackCall对象。
Call<List<Repo>> repos = service.listRepos("octocat");
发起请求
由上一节可知,这里的call对象类型为ExecutorCallbackCall。
call.enqueue(new Callback<List<Object>>() {
@Override
public void onResponse(@NonNull Call<List<Repo>> call, @NonNull Response<List<Repo>> response) {
}
@Override
public void onFailure(@NonNull Call<List<Repo>> call, @NonNull Throwable t) {
}
});
ExecutorCallbackCall为DefaultCallAdapterFactory的静态内部类,这里采用类装饰器的思想。
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
delegate.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(
() -> {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on
// cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
调用enqueue后实际上是调用了delegate的enqueue(ps:这里的delegate即为上述所说的OkHttpCall)。而ExecutorCallbackCall在这里的作用是切换线程。callbackExecutor即为在Retrofit初始化时设置的那个,Android平台默认就是主线程。
关于Call、ExecutorCallbackCall、OkHttpCall的关系
classDiagram
Call <|-- ExecutorCallbackCall
Call <|-- OkHttpCall
OkHttpCall <-- ExecutorCallbackCall
OkHttpCall
我们先来看看一个简单的OkHttp用例,对应Retrofit示例代码
// 1. 新建一个Call,ps:注意这里是okhttp3.Call和Retrofit#Call要注意区分
String user = "abc";
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().get()
.url("https://api.github.com/users/" + user + "/repos")
.build();
okhttp3.Call call = client.newCall(request);
// 2. 发起请求
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
}
});
简单描述一下:
- 利用
OkHttpClient新建一个okhttp3.Call对象,注意这里要和Retrofit的Call接口作区分。 - 调用
okhttp3.Call#enqueue发起请求
继续回到OkHttpCall:
// OkHttpCall.java
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
// 1. 新建一个okhttp3.Call
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
// 2. 发起网络请求
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
});
}
上面代码对比纯OkHttp和Retrofit的OkHttpCall就不难理解了。
- 新建一个
okhttp3.Call时,用到之前在ServiceMethod.parseAnnotations创建的RequestFactory对象,主要作用是通过之前保存Method的信息,组装一个okhttp3.Request对象。(ps:数据组装的时候也会使用到Converter)。// OkHttpCall.java private okhttp3.Call createRawCall() throws IOException { okhttp3.Call call = callFactory.newCall(requestFactory.create(args)); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; } - 网络请求响应后,会调用
OkHttpCall#parseResponse,在正常200响应码的情况下就会使用responseConverter转换响应数据。由于我们使用的是// OkHttpCall.java Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // ... ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody); try { T body = responseConverter.convert(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; } }GsonConverterFactory,最终就会到GsonResponseBodyConverter#convert。其实就是使用Gson做一次json解析。ps:这里解析的// GsonResponseBodyConverter.java @Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { T result = adapter.read(jsonReader); if (jsonReader.peek() != JsonToken.END_DOCUMENT) { throw new JsonIOException("JSON document was not fully consumed."); } return result; } finally { value.close(); } }T类型即为刚开始定义的List<Repo>。
设计模式
总结一下本文所遇到的设计模式思想(欢迎在评论补充和修正):
Retrofit的创建用到了外观模式、构造器模式(Builder模式)。Retrofit#create用到了动态代理,将定义的interface实例成一个虚构的对象。- 关于
CallAdatper、Converter的获取用到了策略模式。- 根据定义方法的返回类型(譬如:
Call),获取合适的CallAdatper - 根据响应数据需要转换的数据类型(譬如:
Call<T>中的T),获取合适的responseConverter。
- 根据定义方法的返回类型(譬如:
CallAdatper、Converter的创建用到了工厂模式(CallAdatper.Factory、Contercer.Factory)。CallAdatper用到了适配器模式,即封装了OkHttp能力的OkHttpCall可以适配不同库的调用,譬如ExectorCallbackCall/RxJava/Java8等。Converter的思想也是如此。
最后
本文主要通过Retrofit文档给出的示例代码,简单解析了对于OkHttp整个网络请求的流程封装。阅读源码时,需要注意定义的接口方法对应会选择哪个CallAdapter和哪个Converter,还有Call和okhttp3.Call的混淆等问题。了解整个流程后对于查看其他扩展(譬如RxJava)会有所帮助。
参考文章: