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結構都大同小異:
這種結構在我接觸的項目中是非常的常見,整個結構中只有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請求,減輕服務器壓力 ??