在開始以前,先介紹你們去學習一下RxJava,這個是真的灰常有用的一個庫,Rx系列的都很不錯,這裏有兩個版本,他們之間的方法稍微改變了一下,其餘都差很少:java
給初學者的RxJava2.0教程系列react
給Android開發者的RxJava1.0詳解android
若是你上週沒跟着LZ一塊兒擼的話,那麼請移步:
一步一步帶你認識MVP+Retrofit+Rxjava並封裝(一)git
一、導包:github
//網絡請求 retrofit+okhttp+gson
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'com.squareup.okhttp3:okhttp:3.8.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'複製代碼
二、封裝:json
咱們現來看一下,一個完整的
Retrofit+Rxjva
的請求:後端
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
builder.connectTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
//設置攔截器
builder.addInterceptor(new BasicParamsInterceptor.Builder().addParamsMap(getCommonMap()).build());
builder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
OkHttpClient okHttpClient = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
ApiService api=retrofit.create(ApiService.class);
api.login(username,password)
.subscribeOn(Schedulers.io()) //在IO線程進行網絡請求
.observeOn(AndroidSchedulers.mainThread()) //回到主線程去處理請求結果
.subscribe(new Observer<LoginResponse>() {
@Override
public void onSubscribe(Disposable d) {
//爲請求提供一個取消的手段
}
@Override
public void onNext(LoginResponse value) {
//請求成功
}
@Override
public void onError(Throwable e) {
//請求出錯
}
@Override
public void onComplete() {
//請求完成
}
});複製代碼
考慮到每次請求接口的時候都須要去實例化一個Retrofit對象,並且每次都須要用RxJava來進行線程的切換,所以我就想到把它們都寫到一個基類裏面去。api
public abstract class BaseRetrofit {
protected Retrofit mRetrofit;
private static final int DEFAULT_TIME = 10; //默認超時時間
private final long RETRY_TIMES = 1; //重訂閱次數
public BaseRetrofit() {
//建立okHttpClient
if (null == mRetrofit) {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
builder.connectTimeout(DEFAULT_TIME, TimeUnit.SECONDS);
//設置攔截器
builder.addInterceptor(new BasicParamsInterceptor.Builder().addParamsMap(getCommonMap()).build());
builder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
OkHttpClient okHttpClient = builder.build();
mRetrofit = new Retrofit.Builder()
.baseUrl(HttpServletAddress.getInstance().getServletAddress())
.client(okHttpClient)
.addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
}複製代碼
而後結合咱們上週講的
MVP
,讓BaseModel
繼承它,而後調用方法進行請求,上週咱們尚未細節講它是怎麼樣進行網絡請求的,回到剛纔那個完整的請求的例子,能夠看到,這裏發起請求須要兩個東西,一個Observer
,另外一個是api.login()
的返回值Observable
,這就是大佬們口中說的觀察者和被觀察者,他們之間有一個很微妙的關係,叫訂閱;被觀察者負責網絡請求,觀察者負責觀察網絡請求的回調,每發生一次接口請求,都會有訂閱發生,因此在這裏我把訂閱公共的邏輯放到了BaseRetrofit
中:bash
protected <T> void toSubscribe(Observable<T> observable, Observer<T> observer) {
observable.subscribeOn(Schedulers.io()) // 指定subscribe()發生在IO線程
.observeOn(AndroidSchedulers.mainThread()) // 指定Subscriber的回調發生在io線程
.timeout(DEFAULT_TIME, TimeUnit.SECONDS) //重連間隔時間
.retry(RETRY_TIMES)
// .repeatWhen(new Function<Observable<Object>, ObservableSource<?>>() {
// @Override
// public ObservableSource<?> apply(@NonNull Observable<Object> objectObservable) throws Exception {
// return null;
// }
// })
.subscribe(observer); //訂閱
}複製代碼
這樣每次咱們組裝好
Observable
和Observer
以後就調用這個方法進行訂閱就行了。這裏我有一個困惑,已經好久了,但願知道的讀者能幫忙解惑,重寫retryWhen的時候,如何根據錯誤類型進行重試
。講到這裏可能有人就要問了,LZ
你不仍是沒有講是怎麼進行網絡請求的嗎?大兄弟別急,我這就告訴你,它是經過自定義接口的形式來進行網絡請求的,好吧,說了好像也白說,換個場景你自個去深刻了解去吧:
Retrofit網絡請求框架的基本使用網絡
好了,接着咱們下面的封裝:
被觀察者已經看成接口被咱們處理掉了,那麼下面咱們重點關注觀察者;好久以前我老大跟我講網絡請求封裝這一塊,他當時說咱們只關注請求成功的數據,其餘的不須要特別關注;首先,咱們得有一套統一的回調樣式,以下:
{
"status":1,
"data":T
"msg":"success"
}複製代碼
因爲咱們這邊都把返回的
json
數據都轉成BaseResponse<T>
格式了,若是大家回調的數據格式不統一的話,那就去找後端撕逼去吧;而後咱們只須要重寫Observer
就好了,Observer
接口中有四個方法,上面例子中咱們簡單介紹了一下,它們的執行順序分別是onSubscribe——>onNext——>onComplete(onError)
,這裏須要簡單提一下,onComplete
和onError
方法兩者不會同時都執行,具體來看一下LZ封裝的:
public abstract class BaseObserver<T> implements Observer<T> {
private static final String TAG = "BaseObserver";
protected abstract void onBaseError(Throwable t);
protected abstract void onBaseNext(T data);
protected abstract boolean isNeedProgressDialog();
protected abstract String getTitleMsg();
private ProgressDialogHandler mProgressDialogHandler;
private BaseImpl mBaseImpl;
public BaseObserver(BaseImpl baseImpl) {
mBaseImpl = baseImpl;
if (null != mBaseImpl) {
if (null == mProgressDialogHandler) {
mProgressDialogHandler = new ProgressDialogHandler(baseImpl.getContext(), true);
}
}
}
private void showProgressDialog() {
if (mProgressDialogHandler != null) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG, getTitleMsg()).sendToTarget();
}
}
private void dismissProgressDialog() {
if (mProgressDialogHandler != null) {
mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
mProgressDialogHandler = null;
}
}
@Override
public void onSubscribe(Disposable d) {
//顯示進度條
if (isNeedProgressDialog()) {
showProgressDialog();
}
if (null != mBaseImpl) {
if (null != d) {
mBaseImpl.addDisposable(d);
}
}
}
@Override
public void onNext(T value) {
//成功
Log.d(TAG, "http is onNext");
if (null != value) {
onBaseNext(value);
}
}
@Override
public void onError(Throwable e) {
//關閉進度條
Log.e(TAG, "http is onError");
if (isNeedProgressDialog()) {
dismissProgressDialog();
}
onBaseError(e);
}
@Override
public void onComplete() {
//關閉進度條
if (isNeedProgressDialog()) {
dismissProgressDialog();
}
}
}複製代碼
這裏考慮到有些界面須要進度框,因此我把這一部分也整合到觀察者裏面,這裏根據外面調用的地方有沒有設置
Title
來判斷是否顯示進度框,而後再進行相應的回調,進度框使用的是系統的ProgressDialog
,固然了,你也能夠自定義一個進度框樣式,詳細見demo
。前面咱們說到咱們只關心成功的數據,失敗的數據咱們須要在內部處理掉,即再封裝一層,吃掉onBaseError
:
public abstract class CygBaseObserver<T> extends BaseObserver<T> {
private static final String TAG = "CygBaseObserver";
private boolean isNeedProgress;
private String titleMsg;
public CygBaseObserver() {
this(null, null);
}
public CygBaseObserver(BaseImpl base) {
this(base, null);
}
public CygBaseObserver(BaseImpl base, String titleMsg) {
super(base);
this.titleMsg = titleMsg;
if (TextUtils.isEmpty(titleMsg)) {
this.isNeedProgress = false;
} else {
this.isNeedProgress = true;
}
}
@Override
protected boolean isNeedProgressDialog() {
return isNeedProgress;
}
@Override
protected String getTitleMsg() {
return titleMsg;
}
@Override
protected void onBaseError(Throwable t) {
StringBuffer sb = new StringBuffer();
sb.append("請求失敗:");
if (t instanceof NetworkErrorException || t instanceof UnknownHostException || t instanceof ConnectException) {
sb.append("網絡異常");
} else if (t instanceof SocketTimeoutException || t instanceof InterruptedIOException || t instanceof TimeoutException) {
sb.append("請求超時");
} else if (t instanceof JsonSyntaxException) {
sb.append("請求不合法");
} else if (t instanceof JsonParseException
|| t instanceof JSONException
|| t instanceof ParseException) { // 解析錯誤
sb.append("解析錯誤");
} else if (t instanceof ApiException) {
if (((ApiException) t).isTokenExpried()) {
sb.append("Token出錯");
}
} else {
FRToast.showToastSafe(t.getMessage());
return;
}
Log.e(TAG, "onBaseError: " + sb.toString());
FRToast.showToastSafe(sb.toString());
}
}複製代碼
最開始的例子當中有一個
Disposable
的概念,這個是用來切斷觀察者與被觀察者之間的關係的,每次請求都會產生一個響應的Disposable
,因此這裏我用了一個接口BaseImpl
的形式來回收它,在產生的地方收集它,在BaseActivity的onDestroy中來回收它,詳細的請參見demo
;好了,到這裏咱們的封裝就完成了90%了,讓咱們回到上一次博客當中,組裝Observable
的時候咱們還進行了一個map
操做:![]()
這裏map就是進行一箇中間的操做,這個操做叫作變換,咱們來看一下HttpFunction的實現是怎樣的:
public class HttpFunction<T> implements Function<BaseResponse<T>, T> {
@Override
public T apply(BaseResponse<T> response) throws Exception {
if (!response.isRequestSuccess()) {
throw new ApiException(response.getStatus(), String.valueOf(response.getMsg()));
}
return response.getData();
}
}複製代碼
相信看完方法的實現你們應該知道這個是幹什麼用的了,沒錯,這個方法就是將
BaseResponse<T>
轉換成T
,由於咱們只關注成功的數據,並且只關注data
裏面的數據,因爲返回的數據是BaseResponse<T>
,而咱們須要關注的數據是T
,因此在這裏須要轉換一下,而後判斷請求是否成功就好了。
#####最後的最後,上面我提到一個問題,就是如何重寫retryWhen方法,根據錯誤類型來對請求進行重試操做,這裏給出最終的代碼(這裏LZ是想在網絡出錯或者鏈接超時的時候進行重試):
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
private int mRetryCount;
@Override
public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
if ((throwable instanceof NetworkErrorException
|| throwable instanceof ConnectException
|| throwable instanceof SocketTimeoutException
|| throwable instanceof TimeoutException) && mRetryCount < 3) {
mRetryCount++;
return Observable.timer(2000, TimeUnit.MILLISECONDS);
}
return Observable.error(throwable);
}
});
}
})複製代碼
好了,到這裏基本的網絡請求封裝就完成了,若是你有更好的方法,請私信我一塊兒交流,同時感謝以上引用到博客的博主。最後的最後,放出最後的代碼,歡迎star
和fork