Android Retrofit 线程切换 笔记

11 阅读4分钟

🎯 核心: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.AndroiddefaultCallbackExecutor() 方法中返回了这个 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
  • 在包装的 CallbackonResponseonFailure 中,通过 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 环境中,可能默认就在后台线程回调(因为没有主线程概念),体现了框架的可移植性。


💡 总结:线程切换的设计精髓

  1. 适配器模式:通过 CallAdapter 将底层的 OkHttpCall 适配成符合业务需求的返回值类型,同时可以在适配过程中加入线程切换逻辑。
  2. 责任分离:网络执行(OkHttpCall)和回调线程(callbackExecutor)被清晰分离。
  3. 可扩展性:开发者可以通过自定义 CallAdapter 实现自己的线程切换策略,比如使用协程、RxJava、LiveData 等。
  4. 平台感知:通过 Platform 抽象,Retrofit 能够为不同运行环境提供合理的默认行为。

所以,Retrofit 的线程切换并非魔法,而是精心设计的委托与适配。理解了这一点,你就能轻松地自定义或扩展它的线程模型,甚至可以在自己的框架中借鉴这种设计。