搭建自己的框架之1:Rxjava2+Retrofit2 實現Android Http請求

vanke service

Demo On GitHub(MVP-Rxjava2-Retrofit2)

前言

本文不是單獨講Retrofit2(我上一文章關于Retrofit2),至于http 的知識可以去看《圖解HTTP》這本簡潔的書,主題是:Rxjava2+Retrofit2 實現Android http 請求。

封裝好后Android 中一個Http請求如下:(onFailed 大部分時候不用重寫,在baseobserver中一般都是toast 提示錯誤。但是需要重新定義網絡請求失敗需要override)

        HttpCall.getApiService().goLoginByRxjavaObserver(loginParams)
                .compose(RxObservableUtils.applySchedulers())
                .compose(bindToLifecycle())    // 用jetpack 的ViewModel和LiveData,這也可省
                .subscribe(new BaseObserver<LoginResult>() {
                    @Override
                    public void onSuccess(LoginResult loginResult) {
                        loginSuccess(loginResult); //沒有冗余數據
                    }
                    //大部分不用override
                    @Override
                    public void onFailure(int code, String message) {
                        super.onFailure(code, message); //簡潔明了的錯誤信息
                    }
                });

其中 .subscribe(new BaseObserver<LoginResult>() 可以知道只是我們需要的http 返回的數據, 范型<T>和我們的Api結構有關,

http Api 返回結果處理

我們項目的api 返回的結構大致如下,我看大家的項目json結構都大同小異:

api result json

這種結構在我接觸的項目中是非常的常見,整個結構中只有result 是不同的(json obgj/array),使用泛型<T>來處理就行.

/**
 * 這個類和具體的業務api 結構有關,本Demo的API 結構大致如下:
 * 
 * Created by anylife.zlb@gmail.com on 2016/7/11.
 */
public class HttpResponse<T> {
    private int code;
    private String error;
    private T result;
        // some set and some get
    public T getResult() {
        return result;
    }

    public void setResult(T result) {
        this.result = result;
    }
}

API 要怎樣定義呢?

其中的Header 中的通用頭部字段Token,UUID,OSVERSION 等通過攔截全局放在Header中。

根據Retrofit的public interface Call<T> extends Cloneable {...}定義,我們知道要定義的Api Service每個http method中retrofit返回的是Call<HttpResponse<T>>, 和rxjava2結合起來返回 基本類型Observable<HttpResponse<T>> 就行了,自己看看Retrofit 的源代碼就知道了。
泛型 T和具體的Api 有關,重點放在下面和Rxjava2 的結合:

public interface ApiService {
    /**
     * 獲取信息,返回Observable 可以知道是和Rxjava 結合起來用的
     */
    @GET("api/lebang/staffs/me/detail")
    Observable<HttpResponse<StaffMsg>> getStaffMsg();

    /**
     * 純的Retrofit 啊
     */
    @POST("api/lebang/oauth/access_token1")
    Call<HttpResponse<LoginResult>> goLoginByRetrofit(@Body LoginParams loginParams);

結合rxjava2 要怎么寫http 請求呢?

我們知道Rxjava 中最簡單的流處理是這樣的:Observable.subscribe(Observer);
這種流式處理很好理解就是http 請求這件事被訂閱觀察了。

我們的重點就是封裝處理一下 Observable 和 Observer
以獲取員工信息為例子就是

 ApiService.getStaffMsg()     //1.這是observable
                  .subscribeOn(Schedulers.io()) //2.請求在IO線程
                  .observeOn(AndroidSchedulers.mainThread());  //3觀察處理數據在主線程
                  .subscribe(new Observer<HttpResponse<StaffMsg>>() { // 4.不解釋
                    @Override
                    public void onSubscribe(Disposable d) {      
                    }
                    @Override
                    public void onNext(HttpResponse<StaffMsg> response) {
                       
                    }
                    @Override
                    public void onError(Throwable e) {

                    }

你要這樣子寫也是沒有關系的,但是很明顯有很多的冗余代碼:

  • HttpResponse<T> 返回的數據我們只想要關心T,HttpResponse 其他部分是一樣的
  • 沒有必要每個接口處理onError ,大部分的Http 的錯誤異常處理是可以抽取出共性的
  • 訂閱的線程和觀察的線程都是一樣的,也是重復的代碼
  • 請求的時候需要一個網絡請求的ProgressDialog 的提示怎么辦?(你可能會說V和M有耦合)

綜合上面,我們要再封裝 Observer

先看看Rxjava2 中怎樣定義的Observer

public interface Observer<T> {
    void onSubscribe(Disposable d);

    void onNext(T t);

    void onError(Throwable e);

    void onComplete();
}

那么我們結合 HttpRespose<T> 就知道怎么封裝我們的BaseObserver 了 (僅供參考)

public abstract class BaseObserver<T> implements Observer<HttpResponse<T>> {
    private final String TAG = BaseObserver.class.getSimpleName();
    private final int RESPONSE_CODE_OK = 0;       //自定義的業務邏輯,成功返回積極數據
    private final int RESPONSE_CODE_FAILED = -1;  //返回數據失敗,嚴重的錯誤
    private Context mContext;
    private static Gson gson = new Gson();
    private int errorCode;
    private String errorMsg = "未知的錯誤!";

    /**
     * 根據具體的Api 業務邏輯去重寫 onSuccess 方法!Error 是選擇重寫,but 必須Super !
     * @param t
     */
    public abstract void onSuccess(T t);

    /**
     * @param mContext
     * @param showProgress 默認需要顯示進程,不要的話請傳 false
     */
    public  BaseObserver(Context mContext, boolean showProgress) {
        this.mContext = mContext;
        if (showProgress) {
            HttpUiTips.showDialog(mContext, true, null);
        }
    }
    @Override
    public final void onSubscribe(Disposable d) {
        //不管取消,和生命周期綁定
    }

    @Override
    public final void onNext(HttpResponse<T> response) {
        HttpUiTips.dismissDialog(mContext);
        //根據業務來分
        if (response.getCode() == RESPONSE_CODE_OK) {
            onSuccess(response.getResult());
        } else {
            onFailure(response.getCode(), response.getError());
        }
    }

    @Override
    public final void onError(Throwable t) {
        HttpUiTips.dismissDialog(mContext);
        if (t instanceof HttpException) {
            HttpException httpException = (HttpException) t;
            errorCode = httpException.code();
            errorMsg = httpException.getMessage();
            getErrorMsg(httpException);
        } else if SocketTimeoutException) {  //VPN open
            errorCode = RESPONSE_CODE_FAILED;
            errorMsg = "服務器響應超時";
        } 
        // .....其他的異常處理
        onFailure(errorCode, errorMsg);
    }

    /**
     * 簡單的把Dialog 處理掉
     */
    @Override
    public final void onComplete() {
    }

    /**
     * Default error dispose!
     * 一般的就是 AlertDialog 或 SnackBar
     *
     * @param code
     * @param message
     */
    @CallSuper  //if overwrite,you should let it run.
    public void onFailure(int code, String message) {
        if (code == RESPONSE_CODE_FAILED && mContext != null) {
            HttpUiTips.alertTip(mContext, message, code);
        } else {
            disposeEorCode(message, code);
        }
    }

    /**
     * 對通用問題的統一攔截處理
     * @param code
     */
    private final void disposeEorCode(String message, int code) {
        switch (code) {
            case 101:
            case 401:
                //退回到登錄頁面
                Intent intent = new Intent();
                intent.setClass(mContext, LoginActivity.class);
                mContext.startActivity(intent);
                break;
        }
        Toast.makeText(mContext, message + "   code=" + code, Toast.LENGTH_SHORT).show();
    }


    /**
     * 獲取詳細的錯誤的信息 errorCode,errorMsg ,   這里和Api 結構有關,這里和Api 結構有關 
     * 以登錄的時候的Grant_type 故意寫錯為例子,http 應該是直接的返回401=httpException.code()
     * 但是是怎么導致401的?我們的服務器會在respose.errorBody 中的content 中說明
     */
    private final void getErrorMsg(HttpException httpException) {
        String errorBodyStr = "";
        try {   //我們的項目需要的UniCode轉碼,不是必須要的!
            errorBodyStr = TextUtils.convertUnicode(httpException.response().errorBody().string());
        } catch (IOException ioe) {
            Log.e("errorBodyStr ioe:", ioe.toString());
        }
        try {
            HttpResponse errorResponse = gson.fromJson(errorBodyStr, HttpResponse.class);
            if (null != errorResponse) {
                errorCode = errorResponse.getCode();
                errorMsg = errorResponse.getError();
            } else {
                errorCode = RESPONSE_CODE_FAILED;
                errorMsg = "ErrorResponse is null";
            }
        } catch (Exception jsonException) {
            errorCode = RESPONSE_CODE_FAILED;
            errorMsg = "http請求錯誤Json 信息異常";
            jsonException.printStackTrace();
        }
    }

}

這里的失敗處理和我們的協議有關,比如: 比如oauth(loginin) http 狀態碼是401.實際的信息是在response 的 error body里。
errorCode = httpException.code(); errorMsg = httpException.getMessage();

這樣簡單的封裝后我們的調用就可以非常的簡單了 ,其中onFailure 很多接口都可以不Override,直接的處理onSuccess 返回的數據就行了。
.compose(RxObservableUtils.applySchedulers()) 就處理線程的 切換的,把

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());

變為 一行代碼了,在Activity 中的調用如下,大部分情況不Override onFailure會更加的簡單:

        HttpCall.getApiService().getStaffMsg()
                .compose(RxObservableUtils.applySchedulers())
                .subscribe(new BaseObserver<StaffMsg>(this,true) {
                    @Override
                    public void onSuccess(StaffMsg staffMsg) {
                        
                    }

                    @Override
                    public void onFailure(int code, String message) {
                        super.onFailure(code, message);
                    }
                });

Rxjava 和Retrofit 結合實現Http需要注意

  • 防止內存泄漏,結合RxLifeCycler
  • 傳入context顯示wait dialog易導致內存泄漏,改為虛引用
  • 根據項目的實際情況返回OnFailed 提示信息
  • [全局的處理請求數據 為 Error,Empty,Loading時候的 UI 展示]
    (http://www.lxweimin.com/p/f63015ac4d90)
  • [New]全局的攔截重復的還沒用返回的http請求,減輕服務器壓力 ??

Demo On GitHub (更多請閱讀ReadMe)

下一篇鏈接: 搭建自己的框架之2:MVP+Rxjava2

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容