Demo 地址
相信現(xiàn)在大家都已近在使用 Retrofit + RxJava 框架進行開發(fā),我們也不例外,這里我們不會講如何使用這套框架,而是會講述我在開發(fā)過程中遇到的一個優(yōu)化需求:自定義 Retrofit 的請求接口返回類型,即下面 GitHubService 接口中 listRepos 的返回類型
//官方示例
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
現(xiàn)有的使用方式
在我們的代碼中現(xiàn)在是這樣來定義的:
public interface RestClientV1_0 {
@GET("insurance/month_card/")
Flowable<ResponseBody> getInsuranceCard();
}
ResponseBody 是我們和 API 約定好的數(shù)據(jù)結(jié)構(gòu),大概是這種形式:
public class ResponseBody {
//定義業(yè)務成功或者失敗
private String status;
//Json 格式的字符串,可以反序列化成定義的 Java Bean
private String content;
private String errorCode;
private String errorMsg;
//省略大部分代碼
}
對于 getInsuranceCard() 方法我們使用的形式如下:
DadaApplication.getInstance().getApiV1().getInsuranceCard()
.compose(RxSchedulers.<ResponseBody>io_main(getView(), false))
.as(getView().<ResponseBody>bindAutoDisposable())
.subscribeWith(new ProgressSubscriber<ResponseBody>(getView()) {
@Override
public void onSuccess(ResponseBody response) {
InsuranceCard insuranceCard = response.getContentAs(InsuranceCard.class);
setInsuranceData(insuranceCard);
}
});
在這里我們不關(guān)注 Retrofit 和 RxJava 的使用,可以看見在 onSuccess 回調(diào)方法之前,我們聲明的泛型類型全部為 ResponseBody 類型,在 onSuccess 回調(diào)中我們將 content 這個 Json 字符串解析成 InsuranceCard 對象。
現(xiàn)存問題
由上可知我們現(xiàn)在的使用方式存在兩種問題:
- 在定義接口方法的時候,全部聲明為 ResponseBody 類型,實際上 Api Response 會被解析成什么類型,無法從代碼聲明中得知,而需要去查閱 API 文檔
- 對 Response 的解析是放在 onSuccess() 方法中的,然而我們大部分的 onSuccess() 方法回調(diào)都是在主線程執(zhí)行,當解析數(shù)據(jù)比較大的時候就會造成卡頓
解決思路
最終我們期望對于這款框架的使用變成如下這種形式:
//接口方法的定義
@GET("insurance/month_card/")
DadaFlowable<InsuranceCard> getInsuranceCard();
//接口方法的調(diào)用
DadaApplication.getInstance().getApiV1().getInsuranceCard()
.toFlowable()
.compose(RxSchedulers.<InsuranceCard>io_main(getView(), false))
.as(getView().<InsuranceCard>bindAutoDisposable())
.subscribeWith(new DadaProgressSubscriber<InsuranceCard>(getView()) {
@Override
public void onDadaSuccess(InsuranceCard insuranceCard) {
setInsuranceData(insuranceCard);
}
});
- ResponseBody 對使用者隱藏,只需要看到具體的業(yè)務類型
- 數(shù)據(jù)的解析應該放在子線程中
實際做了哪些
- 自定義
DadaFlowable<T>在定義接口方法時替代Flowable<T>類型 - 重新定義
ApiResponse<T>用來替代原先的ResponseBody類型 - 自定義
Converter用來將 API 返回的 Response 轉(zhuǎn)換成我們需要的ApiResponse<T>類型 - 自定義
CallAdapter來提取ApiResponse<T>中實際的業(yè)務類型 T
大致通過以上四步就可以實現(xiàn)我們的需求,下面我們來具體看一看這四步分別都做了些什么
自定義 DadaFlowable<T>
由于現(xiàn)階段我們只會對新的接口采用這種新的方式,原有的 Flowable<ResponseBody> 的形式仍然保留,因此我們需要自定義一個 DadaFlowable<T> 對象,其內(nèi)部仍然是生成一個 Flowable<T> 對象,如果依然在定義接口方法時使用 Flowable<T> 類型的話,它將會匹配到官方的 RxJava2CallAdapter (有關(guān)于 Retrofit 如何選擇 CallAdapter 以及 Convert 請自行閱讀 ServiceMethod 類的源碼,這里我也附上一篇 Retrofit 非常好的源碼解析 Android:手把手帶你 深入讀懂 Retrofit 2.0 源碼) 而無法匹配到我們接下來自定義的 CallAdapter
DadaFlowable<T> 的代碼目前十分簡單:
public class DadaFlowable<T> {
private final Flowable<T> flowable;
public DadaFlowable(Flowable<T> flowable) {
this.flowable = flowable;
}
public Flowable<T> toFlowable() {
return flowable;
}
}
自定義 ApiResponse<T>
ApiResponse 的定義就更簡單了,幾乎算是對 ResponseBody 代碼的 Copy,只不過我們不在采用字符串的方式來聲明 content 屬性,而是采用泛型的方式:
public class ApiResponse<T> {
public static final String OK = "ok";
private static final String UNKNOWN_ERROR = "unknown_error";
/**
* api 響應狀態(tài) ok 標識成功
*/
private String status;
/**
* api 業(yè)務數(shù)據(jù)
*/
private T content;
/**
* api 響應錯誤碼
*/
private String errorCode;
/**
* errorCode 對應錯誤信息
*/
private String errorMsg;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public boolean isOk() {
return OK.equals(status);
}
public static <T> ApiResponse<T> unknownError(Throwable error) {
ApiResponse<T> apiResponse = new ApiResponse<>();
apiResponse.setStatus(UNKNOWN_ERROR);
apiResponse.setErrorMsg(error.getMessage());
return apiResponse;
}
}
自定義 Converter
converter 的作用比較簡單,我們可以認為是它將接口返回的數(shù)據(jù)解析成我們需要的 Java Bean 對象:
public class FastJsonResponseBodyConverter<T> implements Converter<ResponseBody, ApiResponse<T>> {
private final Type type;
public FastJsonResponseBodyConverter(Type type) {
this.type = type;
}
@Override
public ApiResponse<T> convert(ResponseBody value) throws IOException {
try {
ApiResponse apiResponse = JSON.parseObject(value.string(), ApiResponse.class);
Object content = apiResponse.getContent();
if (apiResponse.isOk() && JSONObject.class != type && JSONArray.class != type && null != content) {
apiResponse.setContent(JSON.parseObject(content.toString(), type));
}
return apiResponse;
} catch (Throwable e) {
e.printStackTrace();
return ApiResponse.unknownError(e);
}
}
}
我們需要關(guān)注的是其中的 convert(ResponseBody value) 方法,它會將接口返回的 Response 解析成 ApiResponse<T>對象并返回,之后我們會講述在何處使用到了這個返回對象
自定義 CallAdapter 相關(guān)
我們定義了一個用于生產(chǎn) CallAdapter 的工廠,我只貼出這個工廠類里面的核心方法:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
if (rawType != DadaFlowable.class) {
return null;
}
//省略...
Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
//省略...
//一般走到這里 responseType 就是我們聲明的業(yè)務類型
responseType = observableType;
return new DadaRxJava2CallAdapter<>(responseType);
}
這里的代碼也比較簡單,我將官方提供的 RxJava2CallAdapterFactory 代碼進行了一些修改和刪減,只有在聲明返回類型為 DadaFlowable<T> 的時候才會匹配到這個工廠,并且生成對應的 DadaRxJava2CallAdapter 對象:
public class DadaRxJava2CallAdapter<R> implements CallAdapter<ApiResponse<R>, Object> {
private final Type responseType;
DadaRxJava2CallAdapter(Type responseType) {
this.responseType = responseType;
}
@Override
public Type responseType() {
return responseType;
}
@Override
public Object adapt(Call<ApiResponse<R>> call) {
Observable<Response<ApiResponse<R>>> responseObservable = new DadaCallExecuteObservable<>(call);
DadaBodyObservable<R> bodyObservable = new DadaBodyObservable<>(responseObservable);
return new DadaFlowable<>(bodyObservable.toFlowable(BackpressureStrategy.LATEST));
}
}
自定義 DadaCallExecuteObservable<T>
DadaCallExecuteObservable 是照搬官方的 CallExecuteObservable 代碼僅僅換了個名字而已,我們主要看它的 subScribeActual 方法:
@Override
protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone ait for each new observer.
Call<T> call = originalCall.clone();
//省略...
try {
Response<T> response = call.execute();
if (!call.isCanceled()) {
observer.onNext(response);
}
if (!call.isCanceled()) {
terminated = true;
observer.onComplete();
}
} catch (Throwable t) {
//省略...
}
}
省略了部分代碼,當我們的下游 Subscriber 訂閱了 Observe 之后,將會調(diào)用 subscribeActual 方法,我們來看看該方法中的幾段重要代碼:
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
//省略...
return parseResponse(call.execute());
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
//省略...
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
//省略...
}
}
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
在執(zhí)行 retrofit 中的 call 對象(實際上是 OkHttpCall 對象)的 execute 方法的時候,實際上最終它會調(diào)用的 okhttp3.Call 對象的 execute 方法幫我們執(zhí)行網(wǎng)絡請求,并且調(diào)用 parseResponse 方法對返回的 response 進行解析,最終調(diào)用到的是我們上面自定義 Converter 對象的 convert 方法,返回了具體的 ApiResponse<T> 對象(這里是對上面介紹自定義 Converter 的應用)。
由此可知 Response<T> response = call.execute(); 中的 response 對象其實就是 Response<ApiResponse<某種業(yè)務類型>> 對象
在獲取到 response 對象之后,我們將調(diào)用 observer.onNext(response); 方法
自定義 DadaBodyObservable<T>
由 DadaRxJava2CallAdapter 的 adapt 方法可知,我們實際上是用 DadaBodyObservable 來構(gòu)造出一個 DadaFlowable 對象并且返回的,DadaBodyObservable 的代碼很簡單,它其實就一個代理,當我們在最外層使用 DadaFlowable.toFlowable()...這一套調(diào)用流程的時候會先調(diào)用 DadaBodyObservable 的 subscribeActual 方法,然后將該方法傳入的參數(shù)(實際上就是在上面解決思路段落中的 DadaProgressSubscriber 對象)包裝成 BodyObserver 對象然后對 DadaCallExecuteObservable 進行訂閱,代碼如下:
final class DadaBodyObservable<T> extends Observable<T> {
private final Observable<Response<ApiResponse<T>>> upstream;
DadaBodyObservable(Observable<Response<ApiResponse<T>>> upstream) {
this.upstream = upstream;
}
@Override
protected void subscribeActual(Observer<? super T> observer) {
upstream.subscribe(new BodyObserver<>(observer));
}
}
自定義 BodyObserver<R>
在 DadaCallExecuteObservable 中提到的 observer.onNext(response);方法中的 observer 對象實際上就是 BodyObserver 對象,代碼如下:
private static class BodyObserver<R> implements Observer<Response<ApiResponse<R>>> {
private final Observer<? super R> observer;
private boolean terminated;
BodyObserver(Observer<? super R> observer) {
this.observer = observer;
}
@Override
public void onNext(Response<ApiResponse<R>> response) {
if (response.isSuccessful()) {
ApiResponse<R> apiResponse = response.body();
if (apiResponse.isOk()) {
//業(yè)務 OK
observer.onNext(apiResponse.getContent());
} else {
String apiErrorCode = apiResponse.getErrorCode();
String apiErrorMessage = apiResponse.getErrorMsg();
//業(yè)務失敗
Throwable t = new DadaThrowable(apiErrorCode, apiErrorMessage);
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
} else {
terminated = true;
Throwable t = new HttpException(response);
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
}
}
我們僅關(guān)注 onNext 方法,代碼比較簡單,對接口的請求狀態(tài)和業(yè)務狀態(tài)進行狀態(tài),然后回調(diào)給最外外外層 的 Subscriber 對象實際上是 DadaProgressSubscriber 對象的 onNext 或者 onError 方法
總結(jié)
其實你只要能了解的 RxJava2 的使用,并且閱讀掌握 Retrofit 當中關(guān)于類型轉(zhuǎn)換的源碼,就可以實現(xiàn)這個定制的功能。