RxJava練武場之——基於Observable網絡框架的搭建

RxJava練武場是一個rxjava在項目中應用的小系列,包括:java

Observable網絡框架設計的背景和原則

Observable網絡框架創建的緣由

兩個問題:json

  • Retrofit已經對網絡請求作了封裝,爲何還要封裝?api

    答:網絡請求中對於請求流程、配置、入參封裝、加解密、異常處理每一個app都是固定不變的,若是業務每次請求都本身處理,會存在冗餘代碼,且質量不易保證。因此咱們須要基於Retrofit對這些流程和操做作二次封裝,並對調用方式進行統一,咱們稱之爲網絡框架。bash

  • 框架封裝Observable方式是什麼?服務器

    答:傳統網絡框架封裝方式採用callback/listener異步回調網絡請求結果。可是這種callback的方式,沒有利用到Retrofit的一大優點--rxjava調用,因此咱們要基於rxjava調用方式,封裝一個基於返回Observable的網絡請求框架。 如下所說網絡框架,均指基於返回Observable的網絡請求二次封裝框架。網絡

Observable網絡框架設計目標

  • T1:業務對Request的業務params、url可輕鬆配置,框架對params組合和加密統一處理
  • T2:框架對返回Response解密、code判斷等統一處理,並直接返回業務JavaBean結果
  • T3:返回值Observable,使網絡請求可做爲rxjava調用鏈中的一環
  • T4:調用入口統1、封裝流程,對業務透明
  • T5:請求支持cancle機制、progressBar等通用處理

Observable網絡框架設計原則

設計原則:架構

  1. 網絡框架Api返回Observable對象,並做爲網絡請求事件的生產者:app

    生產者負責請求的發起接收,和返回數據的預處理。框架

  2. 業務註冊Observer類,做爲消費者。異步

    只負責對返回數據的響應

這是爲了T3的設計目標,生產者和消費者界限明確,作到徹底解耦,爲網絡請求與其餘rxjava異步調用的整合打基礎。

這篇文章詳細講述框架中生產者的實現,消費者的實如今RxJava練武場之——Observable網絡框架的解耦和複用中介紹


Observable網絡框架如何實現

在Observable的建立過程當中,框架如何封裝?

首先咱們須要一個Manager或Helper全局句柄,經過他能夠發起網絡請求,通常設計爲單例全局持有,有利於網絡請求一些資源的共用。 咱們暫定爲NetHelper,其網絡請求Api定義爲:

private static <R extends HttpResponse<?>> Observable sendRequest(final HttpRequest<R> orgRequest) 
複製代碼

sendRequest方法中,返回Observable對象,經過泛型規範Response的類型(T2目標的基礎),HttpRequest(T1目標的基礎)

第一步 HttpRequest定義

定義Request接口,這個接口是retrofit指定的方式。Retrofit原本但願你這麼作的:

public interface LoginRequest {

    @POST("{/get/login}")
    Observable<LoginInfo> login(@FieldMap Map<String, String> params);
}
複製代碼

在LoginRequest中你定義了url,Response JavaBean, 入參params的方式(map) 經過 LoginRequest loginRequest = retrofit.create(LoginRequest.class); 動態代理的方式幫你把request的配置都生成好了。

可是map的傳入這部分,須要業務自行處理入參的組合和加密,固然業務能夠經過工具類的方式來規範這一步驟(網上可能是此種方案),但這樣會讓業務參與到請求流程來了,不符合T4的目標。

咱們但願業務定義LoginRequest以下:

public class LoginRequest extends HttpRequest<R extends HttpResponse> {

    private String userAccount;
    private String userPwd;
    private int accountType;

    @Override
    protected String getURL() {
        return "/get/login";
    }
}
複製代碼

業務只關心入參和url的定義,不須要關心入參的組合和加密方式(T1目標),經過NetHelper.send(loginRequest)一個調用就能拿到結果。這種定義方式不只和Retrofit原生方式同樣將url和Response類型都定義了,還一塊兒將params也定義了進來。

因此咱們不直接使用Retrofit的接口定義方式,封裝了一個專爲業務定義的HttpRequest類,最終也由這個類向Retrofit接口傳遞數據。

要知足上述調用方式,知足兩個前提條件

  1. HttRequest基類,要處理入參的組合和加密處理
  2. Retrofit對應的Request定義接口,要改成通用的方式,以適應不一樣的HttpRequest子類的定義

Request接口定義以下:

public interface Request {

    @POST("{url}")
    Observable<JSONObject> postJSONResult(@Path(value="url",encoded =true) String url, @FieldMap Map<String, String> params);
}

複製代碼

咱們定義的Observable泛型是通用的JSONObject,url是經過入參方式傳入。

第二步 建立retrofit;

NetHelper.java中

// 初始化okhttp
OkHttpClient client = new OkHttpClient.Builder()
        .build();
/**
 *OkHttpClient每次請求的時候都要建立,注意:OkHttpClient.Builder()中有ConnectionPool做爲OkHttp的   	*鏈接池要複用,不然請求過多時容易致使內存溢出
**/

// 初始化Retrofit
retrofit = new Retrofit.Builder()
        .client(client)
        .baseUrl(Request.HOST)
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(MyConverterFactory.create())
        .build();

複製代碼

通常addConverterFactory配置是GsonConverterFactory,用於把Response經過GSon轉爲javaBean(Request接口中定義的類型),因爲咱們定義爲了JsonObject,因此咱們使用MyConverterFactory自定義實現

第三步 生成Observable

Observable的真正建立,咱們放到Request基類中

public abstract class HttpRequest<R extends HttpResponse>{
    protected abstract String getURLAction();
    
    //Observable的建立
    public Observable getObservable(Retrofit retrofit) {
        Request request = retrofit.create(Request.class);
		return request.postJSONResult(getURLAction(),getURLParam());
    }
    
    //子類中複寫該接口的url
    public String getBaseURL(){
        //return base url;
    }
    
    public SortedMap<String, String> getURLParam() {
    	//return 對HttpRequest子類(業務類),定義的params,進行組合和加密
    	//通用的組合和加密邏輯就在此處。
    }
    
    //response類型解析
    protected Type type;

    public ObservableMapiRequest(BaseContext context) {
        super(context);
        initType();
    }

    //泛型R類型的獲取,拿到HttpResponse的類型
    private void initType() {
        Type superClass = getClass().getGenericSuperclass();

        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

    }

    public Type getType() {
        return type;
    }
}
複製代碼

這一步中,咱們將HttpRequest子類(例如LoginRequest)中定義的url params HttpResponse都獲取到了。

第四步 NetHelper Api的調用

以上三步,已經初步將Observable返回。下面的處理NetHelper中Api的調用,也是框架的重點和核心:

private static <R extends HttpResponse<?>> Observable sendRequest(final ObservableRequest<R> orgRequest){
        return NetHelper.getObservable(orgRequest)
                //對Response的JsonObject進行類型轉化
                .map(new JavaBeanFunc(orgRequest.getType()))
                //添加攔截器、Log等
                .filter(new LogInterceptor(orgRequest))
                //對Response預處理,code分類等
                .compose(ResponseTransformer.handleResult())
                //配置線程
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());

    }
複製代碼

NetHelper.getApiObservable方法後,再加上網絡請求的線程配置,這時候業務subscribe消費者,就能夠直接獲得解密後的JsonObject了。注意此時只是JSONObject,咱們須要轉換成咱們須要的Response。

咱們在HttpRequest類中看到了,定義HttpRequest子類時,是須要傳入Response類型的(就是R),在HttpRequest類中已經將Response的類型解析出來,並保存了。在JavaBeanFunc中進行了轉化

public class JavaBeanFunc<J extends JSONObject,T extends HttpResponse> implements Function<J,T> {
    Type mCon;
    public JavaBeanFunc(Type con){
        mCon = con;
    }
    @Override
    public T apply(J jsonObject) throws Exception {
        if (jsonObject != null){
            T response = jsonObject.toJavaObject(mCon);
            return response;
        } else {
            return null;
        }
    }
}
複製代碼

同時配置了攔截器,用於log輸出,和異常的攔截,也能夠作網絡性能指標的記錄等

public class LogInterceptor<R extends HttpResponse<?>> implements Predicate<R> {
    private HttpRequest<R> orgRequest;

    public LogInterceptor(HttpRequest<R> request){
        orgRequest = request;
    }

    @Override
    public boolean test(R response) throws Exception {
        boolean isPredicate = false;
        if (orgRequest != null && response != null) {
            HttpHelper.printHttpLog(orgRequest, JSONObject.toJSONString(response));
            isPredicate = true;
        }
        return isPredicate;
    }
}
複製代碼

response解析完畢以後,尚未完事;必須對response的正常和異常兩種狀況作一個判斷,哪些狀況做爲onNext傳遞,哪些狀況做爲onError傳遞,也要定義清楚。此處咱們採用ObservableTransformer來對數據流處理。

有些文章對於progressbar的控制也放到這裏,我認爲並不符合框架的設計原則,也就沒法實現目標T3,不屬於生產者該作的事情。

public static <T extends Serializable> ObservableTransformer<T, T> handleResult() {
    return upstream -> upstream
            .onErrorResumeNext(new ErrorResumeFunction<T>())
            .flatMap(new ResponseFunction<T>());
}

private static class ErrorResumeFunction<T extends Serializable> implements Function<Throwable, ObservableSource<? extends T>> {

    @Override
    public ObservableSource<? extends T> apply(Throwable throwable) throws Exception {
        return Observable.error(CustomException.handleException(throwable));
    }
}

private static class ResponseFunction<T extends Serializable> implements Function<T, ObservableSource<T>> {

    @Override
    public ObservableSource<T> apply(T tResponse) throws Exception {
        int code = tResponse.getCode();
        String message = tResponse.getMsg();

        if (code == SUCCESS.value()) {
            return Observable.just(tResponse);
        } else {
            return Observable.error(new ApiException(code, message));
        }
    }
}
複製代碼

至此,你可能有兩個疑問,一個是response中code的斷定能夠在observer中處理嗎,另外一個是服務器錯誤和業務錯誤爲什麼都做爲error拋出。

第一個問題: code值的斷定不能夠在observer中處理,而必須在Observable一端處理。由於Observable形式的網絡請求是做爲數據流中的一環出現的,可能當前網絡請求只是一連串異步調用(rxjava調用)的一環。這是實現目標T3的關鍵點。 第二個問題: response中code!=SUCCESS是業務錯誤的狀況,必須向數據流中發出,讓業務處理此異常。(那同時對於Response的定義也是,code!=SUCCESS必須是不須要業務處理的狀況才行) 兩種錯誤都拋出error(內部code不一樣),方便架構使用者在事件響應時,既能捕捉全部錯誤,又能區分錯誤的類型。

框架的封裝性和Request cancle機制

框架的封裝性

看下最終使用的狀況

LoginRequest request = new LoginRequest();
request.setUserAccount("888");
request.setUserPwd("888");

ApiHelper.send(request)
    .subscribe(new Consumer<LoginResponse>() {
                    @Override
                    public void accept(LoginResponse s) throws Exception {

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

                    }
                });
複製代碼

業務在使用上很簡潔,LoginRequest和LoginResponse的定義也是業務關注的內容。基本達到T4的目標

Request的Cancle機制

Request的cancle機制,通常原理是這樣:在Activity/Fragment關閉時,須要cancle調正在請求中的網絡,因此須要BaseActivity/BaseFragment的支持。 方案有二,這篇文章就不贅述了,由於都是比較成熟的方案。

  1. 框架中加入RxLifecycle的綁定,這樣在調用NetHelper的Api時,要傳入BaseActivity/BaseFragment的實例
  2. 本身實現:在框架返回Observable的doOnSubscribe方法中,對當前request的disposable進行保存,並設置tag保存disposable和activity的對應關係,在BaseActivity的onDestroy方法中統一對disposable進行清理。

框架基於Rxjava的鏈式調用

咱們經常遇到這樣一種場景,一個網絡請求每每基於上一個網絡請求的結果,或者一個頁面的數據來自兩個網絡請求的組合,咱們的網絡框架是如何知足這種需求的呢?這也是咱們這套框架設計的出發點和落腳點:

RegisterRequest registerRequest = new RegisterRequest();
registerRequest.setUserAccount(account.getText());
registerRequest.setUserPwd(pwd.getText());
registerRequest.setUserSex(sex.getType());

ApiHelper
    .send(registerRequest)
    .flatMap(new Function<RegisterResponse, ObservableSource<HttpResponse<LoginResponse>>>() {
            @Override
            public ObservableSource<HttpResponse<LoginResponse>> apply(RegisterResponse response) throws Exception {
                LoginRequest loginRequest = new LoginRequest();
                loginRequest.setUserAccount(account.getText());
                loginRequest.setUserPwd(pwd.getText();
                return ApiHelper.send(loginRequest);
                
            }
    })
    .subscribe(new Consumer<LoginResponse>() {
                    @Override
                    public void accept(LoginResponse s) throws Exception {

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

                    }
            });
複製代碼

這樣咱們就完成了一個先註冊後登錄的網絡請求流程,將網絡框架融入了rxjava的調用鏈之中。

總結

這篇文章着重對基於Observable的網絡框架中,Observable生產者部分進行了闡述,在‘框架的封裝性’一節所舉的例子中,咱們只採用了最普通的Consumer來進行事件的消費,咱們在下一篇文章中RxJava練武場之——Observable網絡框架的解耦和複用會專門對消費者進行封裝,使該框架的規範性和擴展性更強。

相關文章
相關標籤/搜索