Retrofit 源码分析(二)- 多样的CallAdapter(RxJava2/Java8)

1,358 阅读8分钟

前言

前文再续,书接上回。上篇文章解析了有关Retrofit网络请求流程设计。了解代码执行的各阶段Retrofit的设计。本文将聚焦在CallAdapter,讲讲结合RxJava/Java8后,Retrofit的使用变化。以及对于RxJava/Java8的适配器源码。

ps:本文只针对RxJava2讲解

RxJava2

本小节涉及到的Gradle依赖:

implementation 'com.squareup.retrofit2:retrofit:2.9.0' 
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

第一个出场的是RxJava2。添加以上关于RxJavaAdapter的依赖后,可以在初始化Retrofit时添加以下代码,添加一个关于RxJava2CallAdapter.Factory

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


// RxJava2CallAdapterFactory.java
public static RxJava2CallAdapterFactory create() {
  return new RxJava2CallAdapterFactory(null, false);
}

private RxJava2CallAdapterFactory(@Nullable Scheduler scheduler, boolean isAsync) {
  this.scheduler = scheduler;
  this.isAsync = isAsync;
}

RxJava2CallAdapterFactory构造方法有两个参数

  • scheduler可以指定subscribeOn的线程调度
  • isAsync对应的是OkHttp同步执行和异步执行。ps:本文只针对同步执行解析

于是,网络请求接口可以定义成这样:

@GET("users/{user}/repos")
Observable<List<Repo>> listReposRx(@Path("user") String user);

RxJava2CallAdapterFactory

上述我们为接口的返回类型声明为Observable,在loadServiceMethod中会匹配到RxJava2CallAdapterFactory,调用其get方法,获取到一个RxJava2CallAdapter对象。

// RxJava2CallAdapterFactory.java
public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  Class<?> rawType = getRawType(returnType);

  if (rawType == Completable.class) {
    // Completable is not parameterized (which is what the rest of this method deals with) so it
    // can only be created with a single configuration.
    return new RxJava2CallAdapter(
        Void.class, scheduler, isAsync, false, true, false, false, false, true);
  }

  boolean isFlowable = rawType == Flowable.class;
  boolean isSingle = rawType == Single.class;
  boolean isMaybe = rawType == Maybe.class;
  if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
    return null;
  }

  boolean isResult = false;
  boolean isBody = false;
  Type responseType;
  if (!(returnType instanceof ParameterizedType)) {
    String name =
        isFlowable ? "Flowable" : isSingle ? "Single" : isMaybe ? "Maybe" : "Observable";
    throw new IllegalStateException(
        name
            + " return type must be parameterized"
            + " as "
            + name
            + "<Foo> or "
            + name
            + "<? extends Foo>");
  }

  Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
  Class<?> rawObservableType = getRawType(observableType);
  if (rawObservableType == Response.class) {
    if (!(observableType instanceof ParameterizedType)) {
      throw new IllegalStateException(
          "Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
    }
    responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
  } else if (rawObservableType == Result.class) {
    if (!(observableType instanceof ParameterizedType)) {
      throw new IllegalStateException(
          "Result must be parameterized" + " as Result<Foo> or Result<? extends Foo>");
    }
    responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
    isResult = true;
  } else {
    responseType = observableType;
    isBody = true;
  }

  return new RxJava2CallAdapter(
      responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false);
}

从代码中可知,该Adapter还支持RxJava的CompletableFlowableSingleMaybe

RxJava2CallAdapter

然后来看看RxJava2CallAdapter#adapt方法

// RxJava2CallAdapter.java
public Object adapt(Call<R> call) {
  Observable<Response<R>> responseObservable =
      isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);

  Observable<?> observable;
  if (isResult) {
    observable = new ResultObservable<>(responseObservable);
  } else if (isBody) {
    observable = new BodyObservable<>(responseObservable);
  } else {
    observable = responseObservable;
  }

  if (scheduler != null) {
    observable = observable.subscribeOn(scheduler);
  }

  if (isFlowable) {
    return observable.toFlowable(BackpressureStrategy.LATEST);
  }
  if (isSingle) {
    return observable.singleOrError();
  }
  if (isMaybe) {
    return observable.singleElement();
  }
  if (isCompletable) {
    return observable.ignoreElements();
  }
  return RxJavaPlugins.onAssembly(observable);
}
  • 首先会根据isAsync创建一个Observable对象,CallExecuteObservable封装的是OkHttp的同步执行逻辑。当然,还需要将封装了OkHttp请求逻辑的OkHttpCall对象传入。
  • 在根据外部定义的返回类型将Observable作转换。
@GET("users/{user}/repos")
Observable<List<Repo>> listReposRx(@Path("user") String user);

所以在最开始定义的接口方法中,返回的Observable对象其实是一个CallExecuteObservable/CallEnqueueObservable

CallExecuteObservable

先来看一下,外部执行网络请求时

Observable<List<Repo>> observable = service.listReposRx("octocat");
observable.subscribe(new Consumer<List<Object>>() {
    @Override
    public void accept(List<Repo> objects) throws Exception {

    }
}, new Consumer<Throwable>() {
    @Override
    public void accept(Throwable throwable) throws Exception {

    }
}, new Action() {
    @Override
    public void run() throws Exception {

    }
});

再来看看,CallExecuteObservable的逻辑

// CallExecuteObservable.java
final class CallExecuteObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;

  CallExecuteObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override
  protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    Call<T> call = originalCall.clone();
    CallDisposable disposable = new CallDisposable(call);
    observer.onSubscribe(disposable);
    if (disposable.isDisposed()) {
      return;
    }

    boolean terminated = false;
    try {
      Response<T> response = call.execute();
      if (!disposable.isDisposed()) {
        observer.onNext(response);
      }
      if (!disposable.isDisposed()) {
        terminated = true;
        observer.onComplete();
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (terminated) {
        RxJavaPlugins.onError(t);
      } else if (!disposable.isDisposed()) {
        try {
          observer.onError(t);
        } catch (Throwable inner) {
          Exceptions.throwIfFatal(inner);
          RxJavaPlugins.onError(new CompositeException(t, inner));
        }
      }
    }
  }
...  

外部调用observable.subscribe后最终会走到CallExecuteObservable#subscribeActual方法,此时网络请求正式发起。这一块不了解的可以阅读笔者有关RxJava的文章:Rx的Observable.create干了啥?

  • 通过call.execute();同步的方式发起网络请求。
  • 网络请求结果响应后,会在onNext中回调。

至此,网络请求的发起和响应就得到了闭环。

关于线程切换的问题

在之前的那篇文章提到,Retrofit的默认实现是会在响应时切换线程的。还有一个需要补充的是,是否需要切换线程也是根据OkHttp的同步异步决定的,因为在同步执行的过程中OkHttp会在当前线程执行。当然,OkHttp的异步执行还会先做一次maxRequestsmaxRequestsPerHost的校验。

譬如现在的上层代码声明为:

observable
    .subscribeOn(Schedulers.io())
    .subscribe(new Consumer<List<Object>>() {
    @Override
    public void accept(List<Object> objects) throws Exception {
    
    }
}, new Consumer<Throwable>() {
    @Override
    public void accept(Throwable throwable) throws Exception {

    }
}, new Action() {
    @Override
    public void run() throws Exception {

    }
});

那么在CallExecuteObservable#subscribeActual执行时,线程为Schedulers.io()下的线程,而执行call.execute();线程也为Schedulers.io(),在onNext响应时也是同一个线程

image.png

image.png

如果将调用改为异步,可以在最开始初始化Retrofit时改成

.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())

此时CallEnqueueObservable#subscribeActual执行时,线程为Schedulers.io()下的线程,而执行call.enqueue();由于网络请求交给了OkHttp的线程池管理,所以回调线程为OkHttp的线程池下的线程,在onNext响应时也是OkHttp的线程

image.png

image.png

ps:异步执行时,OkHttp会在最大请求总数、请求同一个Host的最大请求数等方面作出限制;同步时则依赖了RxJavasubscribeOn设置的线程池调度。这个可以结合实际情况选择使用。

实现响应回调的线程切换方法,可以在调用时声明一个

implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
observable
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())

这样就可以将onNext回调转换为在主线程执行了。

ps:想了解RxJava的线程切换问题,可以阅读笔者的另一篇文章:RxJava的subscribeOn与observeOn源码解析

一些开发的小技巧

Restful的网络接口设计中,我们一般会向后端约定一个固定的响应格式,如:

{
    "errCode": "",
    "errMessage": "",
    "data": {}
}

这时我们就可以有一个固定的响应数据实体,这个相信很多人都会这样做

public class XResponse<T> {
    private String errCode;
    private String errMessage;
    private T data;

    public String getErrCode() {
        return errCode;
    }

    public void setErrCode(String errCode) {
        this.errCode = errCode;
    }

    public String getErrMessage() {
        return errMessage;
    }

    public void setErrMessage(String errMessage) {
        this.errMessage = errMessage;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

通常情况下,我们也需要有一个公共的地方去判断后端请求的情况。利用RxJava结合这个实体,就可以封装出两种形式的通用层

自定义Observer

abstract class HttpResultObserver<T> extends ResourceObserver<XResponse<T>> {
    @Override
    public void onNext(@NonNull XResponse<T> tResponse) {
        if (tResponse.getErrCode().equals("OK")) {
            onSuccess(tResponse.getData());
        } else {
            onFailed(new Exception(tResponse.getErrMessage()));
        }
    }

    @Override
    public void onError(@NonNull Throwable e) {
        onFailed(new Exception(e));
    }

    @Override
    public void onComplete() {

    }

    abstract void onSuccess(T data);

    abstract void onFailed(Exception e);
}

Observable<XResponse<List<Repo>>> observable = service.listReposRx2("abc");

Disposable disposable = observable.subscribeWith(new HttpResultObserver<List<Repo>>() {
    @Override
    void onSuccess(List<Object> data) {

    }

    @Override
    void onFailed(Exception e) {

    }
});

自定义一个ResourceObserver,在onNext时判断errCode的值,从而判断本次网络请求在业务上的成功失败

自定义Observable

第二种是借鉴RxJava的思想,自定义Observable

public class HttpResultObservable<T> extends Observable<T> {
    final Observable<XResponse<T>> source;

    public HttpResultObservable(Observable<XResponse<T>> source) {
        this.source = source;
    }

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
        HttpResultObserver<T> parent = new HttpResultObserver<>(observer);
        observer.onSubscribe(parent);
        source.subscribe(parent);
    }

    static class HttpResultObserver<T> extends ResourceObserver<XResponse<T>> {
        final Observer<? super T> observer;

        HttpResultObserver(Observer<? super T> observer) {
            this.observer = observer;
        }

        @Override
        public void onNext(@NonNull XResponse<T> txResponse) {
            if (txResponse.getErrCode().equals("OK")) {
                observer.onNext(txResponse.getData());
            } else {
                observer.onError(new Exception(txResponse.getErrMessage()));
            }
        }

        @Override
        public void onError(@NonNull Throwable e) {
            observer.onError(e);
        }

        @Override
        public void onComplete() {
            observer.onComplete();
        }
    }
}

Observable<XResponse<List<Repo>>> observable = service.listReposRx2("abc");

Disposable disposable = new HttpResultObservable<List<Object>>(observable)
        .subscribe(new Consumer<List<Repo>>() {
            @Override
            public void accept(List<Object> objects) throws Exception {

            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {

            }
        });

两种做法都可以返回一个Disposable对象,可以结合Activity生命周期调用dispose(),及时避免内存泄漏的问题。

Java8

笔者目前日常开发Android大多会使用Kotlin,而且目前Retrofit + RxJava比较主流。但考虑到Java8其实已经是比较老的版本了,基于它的新特性还是要了解的。更何况在最新的Gradle7.0已经要求使用JDK11了。所以本文就顺便解析一下Retrofit中关于Java8CallAdapter适配吧。

完整的Java8调用网络请求代码:

CompletableFuture<List<Repo>> completableFuture = service.listReposJava8("octocat");
try {
    List<Repo> list = completableFuture.get();
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}

在最新版本的Retrofit默认支持Java8,需要Android SDK在24及以上。在Retrofit.Builder#build中会加入一个CompletableFutureCallAdapterFactory

ps:CompletableFutureJava8新加入的工具。

// Platform.java
static final class Android extends Platform {
  Android() {
    super(Build.VERSION.SDK_INT >= 24);   // hasJava8Types在24及以上为true
  }
...
}

List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
    @Nullable Executor callbackExecutor) {
  DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
  return hasJava8Types
      ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
      : singletonList(executorFactory);
}

CompletableFutureCallAdapterFactory

// CompletableFutureCallAdapterFactory.java
public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  if (getRawType(returnType) != CompletableFuture.class) {
    return null;
  }
  if (!(returnType instanceof ParameterizedType)) {
    throw new IllegalStateException(
        "CompletableFuture return type must be parameterized"
            + " as CompletableFuture<Foo> or CompletableFuture<? extends Foo>");
  }
  Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);

  if (getRawType(innerType) != Response.class) {
    // Generic type is not Response<T>. Use it for body-only adapter.
    return new BodyCallAdapter<>(innerType);
  }

  // Generic type is Response<T>. Extract T and create the Response version of the adapter.
  if (!(innerType instanceof ParameterizedType)) {
    throw new IllegalStateException(
        "Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
  }
  Type responseType = getParameterUpperBound(0, (ParameterizedType) innerType);
  return new ResponseCallAdapter<>(responseType);
}

CompletableFutureCallAdapterFactory#get中,根据返回参数CompletableFuture及其范型类型返回不同的CallAdapter。这里我们以BodyCallAdapter为例。

BodyCallAdapter

// CompletableFutureCallAdapterFactory.java
private static final class BodyCallAdapter<R> implements CallAdapter<R, CompletableFuture<R>> {
  private final Type responseType;

  BodyCallAdapter(Type responseType) {
    this.responseType = responseType;
  }

  @Override
  public Type responseType() {
    return responseType;
  }

  @Override
  public CompletableFuture<R> adapt(final Call<R> call) {
    CompletableFuture<R> future = new CallCancelCompletableFuture<>(call);
    call.enqueue(new BodyCallback(future));
    return future;
  }

  @IgnoreJRERequirement
  private class BodyCallback implements Callback<R> {
    private final CompletableFuture<R> future;

    public BodyCallback(CompletableFuture<R> future) {
      this.future = future;
    }

    @Override
    public void onResponse(Call<R> call, Response<R> response) {
      if (response.isSuccessful()) {
        future.complete(response.body());
      } else {
        future.completeExceptionally(new HttpException(response));
      }
    }

    @Override
    public void onFailure(Call<R> call, Throwable t) {
      future.completeExceptionally(t);
    }
  }
}

adapt可以看到,最终会返回一个CallCancelCompletableFuture对象,及上层调用的返回类型实际上是这个。该类只是对网络请求的取消进行处理,这里不作解释。

call为OkHttpCall,在这个场景下,调用adapt方法,网络请求就会发起(call.enqueue

响应后,会分别调用CompletableFuture的对应方法,表示操作完成或失败

获取请求结果

try {
    List<Repo> list = completableFuture.get();
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}
  • completableFuture.get();方法会一直阻塞,直到上面的complete/completeExceptionally回调。
  • 出现错误后,就会抛出ExecutionException/InterruptedException

ps:CompletableFuture是一个异步转同步的工具,completableFuture.get();调用仍然在当前线程,内部的网络请求因为调用的是异步,所以走的是OkHttp线程池下的线程get方法内部有类似自旋的设计,会一直循环到OkHttp的线程请求响应后获取结果。

最后

本文介绍了有关Retrofit多样的CallAdapter,主要关注于RxJava2/Java8的适配。Retrofit内部还有很多有趣的设计,有时间的话非常建议您去自己阅读其中的逻辑,这会对自己的编程思想比较有帮助。