前言
前文再续,书接上回。上篇文章解析了有关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时添加以下代码,添加一个关于RxJava2的CallAdapter.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的Completable、Flowable、Single和Maybe。
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的异步执行还会先做一次maxRequests和maxRequestsPerHost的校验。
譬如现在的上层代码声明为:
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响应时也是同一个线程。
如果将调用改为异步,可以在最开始初始化Retrofit时改成
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
此时CallEnqueueObservable#subscribeActual执行时,线程为Schedulers.io()下的线程,而执行call.enqueue();由于网络请求交给了OkHttp的线程池管理,所以回调线程为OkHttp的线程池下的线程,在onNext响应时也是OkHttp的线程。
ps:异步执行时,OkHttp会在最大请求总数、请求同一个Host的最大请求数等方面作出限制;同步时则依赖了RxJava的subscribeOn设置的线程池调度。这个可以结合实际情况选择使用。
实现响应回调的线程切换方法,可以在调用时声明一个
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中关于Java8的CallAdapter适配吧。
完整的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:CompletableFuture是Java8新加入的工具。
// 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内部还有很多有趣的设计,有时间的话非常建议您去自己阅读其中的逻辑,这会对自己的编程思想比较有帮助。