🎯 核心:CallAdapter 与 ExecutorCallbackCall
线程切换的关键在于 Retrofit 的**返回值适配器(CallAdapter)**机制。我们一步步拆解。
1. 默认的 CallAdapter 从哪来?
当我们这样构建 Retrofit 时:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
注意,我们没有调用 .addCallAdapterFactory(...),那么 Retrofit 会使用平台默认的 CallAdapter.Factory。在 Android 上,这个默认工厂就是 ExecutorCallAdapterFactory。
查看 Retrofit.Builder.build() 方法源码(简化):
public Retrofit build() {
// ...
// 如果没有添加任何 CallAdapterFactory,则添加平台默认的
if (callAdapterFactories.isEmpty()) {
this.callAdapterFactories = platform.defaultCallAdapterFactories(callbackExecutor);
} else {
this.callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
}
// ...
}
而 Platform 在 Android 上的实现 Platform.Android 中,defaultCallAdapterFactories 方法会返回包含 ExecutorCallAdapterFactory 的列表,并传入一个主线程执行器。
2. 主线程执行器:MainThreadExecutor
Platform.Android 内部定义了 MainThreadExecutor:
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
这个 Executor 简单直接:通过主线程的 Handler 将任务 post 到主线程执行。
Platform.Android 在 defaultCallbackExecutor() 方法中返回了这个 MainThreadExecutor,并传递给 ExecutorCallAdapterFactory。
3. ExecutorCallAdapterFactory 的工作
ExecutorCallAdapterFactory 是一个 CallAdapter.Factory,它负责为返回类型是 Call<T> 的方法创建 CallAdapter。它的 get 方法大致如下:
@Override
public @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
// 关键:将原始的 OkHttpCall 包装成 ExecutorCallbackCall
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
注意这里的 callbackExecutor 就是在创建工厂时传入的(即主线程执行器)。
4. ExecutorCallbackCall:代理类实现线程切换
ExecutorCallbackCall 是 Retrofit 内部的一个静态类,它实现了 Call 接口,内部持有真正的 delegate(即 OkHttpCall)和 callbackExecutor。
它的 enqueue 方法实现如下:
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) {
delegate.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override
public void run() {
if (delegate.isCanceled()) {
// 如果已经被取消,则回调 onFailure
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(new Runnable() {
@Override
public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
// 其他方法(execute、isExecuted、cancel等)直接委托给 delegate
}
可以看到:
- 当我们调用
ExecutorCallbackCall.enqueue(callback)时,它内部调用delegate.enqueue,但传入的是一个包装后的Callback。 - 在包装的
Callback的onResponse和onFailure中,通过callbackExecutor.execute(runnable)将最终的用户回调callback的执行切换到指定线程(这里是主线程)。 - 对于同步请求
execute(),ExecutorCallbackCall直接调用delegate.execute(),它会在当前线程执行并返回响应,不涉及线程切换。
这样,Retrofit 就优雅地实现了异步回调自动切换到主线程。
🔄 与其他 CallAdapter 的线程切换
默认的 ExecutorCallAdapterFactory 只是其中一种实现。通过添加其他 CallAdapter.Factory,我们可以实现更灵活的线程切换,比如:
RxJava2CallAdapterFactory
当我们添加 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 后,接口方法可以返回 Observable<T> 或 Flowable<T> 等。
RxJava2CallAdapter 内部会将 OkHttpCall 适配成 Observable,并可以通过 subscribeOn() 和 observeOn() 自由切换线程。
例如:
@GET("users/{user}/repos")
Observable<List<Repo>> listRepos(@Path("user") String user);
调用时:
gitHubService.listRepos("square")
.subscribeOn(Schedulers.io()) // 网络请求在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 回调在主线程
.subscribe(...);
这里的线程切换由 RxJava 的调度器完成,Retrofit 只是把原始 Call 包装成 Observable,然后交给 RxJava 处理。
LiveDataCallAdapterFactory
类似地,如果使用 LiveDataCallAdapterFactory,返回的是 LiveData<ApiResponse<T>>,它内部会在后台线程执行请求,然后通过 LiveData.postValue() 将结果切换到主线程。
📱 平台抽象:Platform 的作用
Retrofit 的线程切换还依赖于它的平台抽象设计。Platform 是一个抽象类,针对不同的 Java 运行时环境(Android、Java 8+、Java 7)有不同的实现。
在 Android 上,Platform.Android 提供了:
- 默认回调执行器:
MainThreadExecutor,用于将回调派发到主线程。 - 默认 CallAdapter 工厂:
ExecutorCallAdapterFactory,它会使用上面的回调执行器。 - 对默认方法的支持(Java 8 的接口默认方法)。
这种设计使 Retrofit 在 Android 上默认就是“主线程回调”,而在普通 Java 环境中,可能默认就在后台线程回调(因为没有主线程概念),体现了框架的可移植性。
💡 总结:线程切换的设计精髓
- 适配器模式:通过
CallAdapter将底层的OkHttpCall适配成符合业务需求的返回值类型,同时可以在适配过程中加入线程切换逻辑。 - 责任分离:网络执行(
OkHttpCall)和回调线程(callbackExecutor)被清晰分离。 - 可扩展性:开发者可以通过自定义
CallAdapter实现自己的线程切换策略,比如使用协程、RxJava、LiveData 等。 - 平台感知:通过
Platform抽象,Retrofit 能够为不同运行环境提供合理的默认行为。
所以,Retrofit 的线程切换并非魔法,而是精心设计的委托与适配。理解了这一点,你就能轻松地自定义或扩展它的线程模型,甚至可以在自己的框架中借鉴这种设计。