Android Retrofit 线程切换 笔记

🎯 核心:CallAdapter 与 ExecutorCallbackCall

线程切换的关键在于 Retrofit 的**返回值适配器(CallAdapter)**机制。我们一步步拆解。

1. 默认的 CallAdapter 从哪来?

当我们这样构建 Retrofit 时:

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

注意,我们没有调用 .addCallAdapterFactory(...),那么 Retrofit 会使用平台默认CallAdapter.Factory。在 Android 上,这个默认工厂就是 ExecutorCallAdapterFactory

查看 Retrofit.Builder.build() 方法源码(简化):

java 复制代码
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

java 复制代码
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 方法大致如下:

java 复制代码
@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 方法实现如下:

java 复制代码
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() 自由切换线程。

例如:

java 复制代码
@GET("users/{user}/repos")
Observable<List<Repo>> listRepos(@Path("user") String user);

调用时:

java 复制代码
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 的线程切换并非魔法,而是精心设计的委托与适配。理解了这一点,你就能轻松地自定义或扩展它的线程模型,甚至可以在自己的框架中借鉴这种设计。

相关推荐
城东米粉儿3 小时前
Kotlin @JvmOverLoads 笔记
android
alexhilton4 小时前
把离线AI代理装进口袋里
android·kotlin·android jetpack
哈哈浩丶4 小时前
ATF (ARM Trusted Firmware) -2:完整启动流程(冷启动)
android·linux·arm开发·驱动开发
哈哈浩丶4 小时前
ATF (ARM Trusted Firmware) -3:完整启动流程(热启动)
android·linux·arm开发
哈哈浩丶4 小时前
OP-TEE-OS:综述
android·linux·驱动开发
恋猫de小郭14 小时前
你是不是觉得 R8 很讨厌,但 Android 为什么选择 R8 ?也许你对 R8 还不够了解
android·前端·flutter
城东米粉儿16 小时前
Android Glide 笔记
android
城东米粉儿16 小时前
Android TheRouter 笔记
android
城东米粉儿1 天前
Android AIDL 笔记
android