RxRetrofit-終極封裝-深刻淺出&網絡請求

背景:

CSDN博客發佈了一系列的RxJava+Retrofit+OkHttp深刻淺出-終極封裝是否是很眼熟,是的仍是它,不過是不一樣的配方。以前發出後收到不少朋友的關注,本來只是本身學習後的一些經驗總結,可是有同窗運用到實戰當中,這讓我很惶恐,全部後續一直更新了不少次版本,有些地方不免有所變更致使以前的博客有所出入,正好最近受到掘金邀請內測博客,因此決定從新寫一版,按照最後迭代完成的封裝詳細的講述一遍,歡迎你們關注!html

注意:因爲本章的特殊性,後續文章比較長並且複雜,涉及內容也不少,因此你們準備好茶水,前方高能預警。java

RxJava+Retrofit+OkHttp深刻淺出-終極封裝android


封裝成果

封裝完之後,具備以下功能:git

1.Retrofit+Rxjava+okhttp基本使用方法
    2.統一處理請求數據格式
    3.統一的ProgressDialog和回調Subscriber處理
    4.取消http請求
    5.預處理http請求
    6.返回數據的統一判斷
    7.失敗後的retry封裝處理
    8.RxLifecycle管理生命週期,防止泄露複製代碼

實現效果:


具體使用

封裝後http請求代碼以下github

// 完美封裝簡化版
    private void simpleDo() {
        SubjectPost postEntity = new SubjectPost(simpleOnNextListener,this);
        postEntity.setAll(true);
        HttpManager manager = HttpManager.getInstance();
        manager.doHttpDeal(postEntity);
    }

    // 回調一一對應
    HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {
        @Override
        public void onNext(List<Subject> subjects) {
            tvMsg.setText("已封裝:\n" + subjects.toString());
        }

        /*用戶主動調用,默認是不須要覆寫該方法*/
        @Override
        public void onError(Throwable e) {
            super.onError(e);
            tvMsg.setText("失敗:\n" + e.toString());
        }
    };複製代碼

是否是很簡單?你可能說這還簡單,好我們對比一下正常使用Retrofit的方法數據庫

/** * Retrofit加入rxjava實現http請求 */  
   private void onButton9Click() {  
       //手動建立一個OkHttpClient並設置超時時間 
       okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder();  
       builder.connectTimeout(5, TimeUnit.SECONDS);  

       Retrofit retrofit = new Retrofit.Builder()  
               .client(builder.build())  
               .addConverterFactory(GsonConverterFactory.create())  
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())  
               .baseUrl(HttpManager.BASE_URL)  
               .build();  

/        加載框  
       final ProgressDialog pd = new ProgressDialog(this);  

       HttpService apiService = retrofit.create(HttpService.class);  
       Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);  
       observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())  
               .subscribe(  
                       new Subscriber<RetrofitEntity>() {  
                           @Override  
                           public void onCompleted() {  
                               if (pd != null && pd.isShowing()) {  
                                   pd.dismiss();  
                               }  
                           }  

                           @Override  
                           public void onError(Throwable e) {  
                               if (pd != null && pd.isShowing()) {  
                                   pd.dismiss();  
                               }  
                           }  

                           @Override  
                           public void onNext(RetrofitEntity retrofitEntity) {  
                               tvMsg.setText("無封裝:\n" + retrofitEntity.getData().toString());  
                           }  

                           @Override  
                           public void onStart() {  
                               super.onStart();  
                               pd.show();  
                           }  
                       }  

               );  
   }複製代碼

可能你發現確是代碼有點多,可是更加可怕的是,若是你一個activity或者fragment中屢次須要http請求,你須要屢次重複的寫回調處理(一個回到就有4個方法呀!!!!反正我是忍受不了),並且以上處理尚未作過多的判斷和錯誤校驗就如此複雜!~好了介紹完了,開始我們的優化之路吧!json


項目結構:


RxJava

若是你對RxJava不瞭解,好吧騷年趕快學學吧,否則真會out了,下面給出博主當初學習RxJava的一些資源:api


Retrofit

咱家今天的主角來了,我們也深刻淺出一下了解下Retrofit使用,前方高能,若是你是深度Retrofit選手請直接跳過本節!!!緩存

1.首先確保在AndroidManifest.xml中請求了網絡權限

<uses-permission android:name="android.permission.INTERNET"/>複製代碼

2.在app/build.gradle添加引用

/*rx-android-java*/
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
    compile 'com.trello:rxlifecycle:1.0'
    compile 'com.trello:rxlifecycle-components:1.0'
    /*rotrofit*/
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0'
    compile 'com.google.code.gson:gson:2.8.0'複製代碼

3.經常使用註解

這裏介紹一些經常使用的註解的使用服務器

  • @Query@QueryMap:用於Http Get請求傳遞參數

  • @Field:用於Post方式傳遞參數,須要在請求接口方法上添加@FormUrlEncoded,即以表單的方式傳遞參數

  • @Body:用於Post,根據轉換方式將實例對象轉化爲對應字符串傳遞參數.好比Retrofit添加GsonConverterFactory則是將body轉化爲gson字符串進行傳遞

  • @Path:用於URL上佔位符

  • @Part:配合@Multipart使用,通常用於文件上傳

  • @Header:添加http header

  • @Headers:跟@Header做用同樣,只是使用方式不同,@Header是做爲請求方法的參數傳入,@Headers是以固定方式直接添加到請求方法上

ReTrofit基本使用:

首先給定一個測試接口文檔,後面的博客中咱們都是用這個接口調試

/** * @api videoLink 50音圖視頻連接 * @url http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink * @method post * @param once_no bool(選填,ture無連接) 一次性獲取下載地址 * @return json array( * ret:1成功,2失敗 * msg:信息 * data:{ * name:視頻名稱 * title:標題 * } )複製代碼

1.初始化retrofit

要向一個api發送咱們的網絡請求 ,咱們須要使用Retrofit builder類並指定servicebase URL(一般狀況下就是域名)。

String BASE_URL = " http://www.izaodao.com/Api/"  
    Retrofit retrofit = new Retrofit.Builder()  
            .baseUrl(BASE_URL)  
            .addConverterFactory(GsonConverterFactory.create())  
            .build();複製代碼

2.設置接口service

注意到每一個endpoint 都指定了一個關於HTTP(GET, POST, 等等。) 方法的註解以及用於分發網絡調用的方法。並且這些方法的參數也能夠有特殊的註解。

/** * 接口地址 * Created by WZG on 2016/7/16. */  
public interface MyApiEndpointInterface {  
    @POST("AppFiftyToneGraph/videoLink")  
    Call<RetrofitEntity> getAllVedio(@Body boolean once_no) }複製代碼

3.獲得call而後同步處理處理回調:

MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);  
Call<RetrofitEntity> call = apiService.getAllVedio(true);  
call.enqueue(new Callback<RetrofitEntity>() {  
    @Override  
    public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {  
        RetrofitEntity entity = response.body();  
        Log.i("tag", "onResponse----->" + entity.getMsg());  
    }  

    @Override  
    public void onFailure(Throwable t) {  
        Log.i("tag", "onFailure----->" + t.toString());  

    }  
});複製代碼

這就是簡單的Retrofit使用步驟,接下來咱們結合RxJava講述

ReTrofit+Rxjava基本使用

對比以前的Retrofit使用

1.在於咱們須要修改service接口返回信息咱們須要返回一個Observable對象

@POST("AppFiftyToneGraph/videoLink")  
Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);複製代碼

2.而後初始化Retrofit須要添加對Rxjava的適配,注意必定要retrofit2纔有這個功能哦

Retrofit retrofit = new Retrofit.Builder()  
                .client(builder.build())  
                .addConverterFactory(GsonConverterFactory.create())  
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())  
                .baseUrl(HttpManager.BASE_URL)  
                .build();複製代碼

3.回調經過RxJava處理

HttpService apiService = retrofit.create(HttpService.class);  
       Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);  
       observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())  
               .subscribe(  
                       new Subscriber<RetrofitEntity>() {  
                           @Override  
                           public void onCompleted() {  
                           }  

                           @Override  
                           public void onError(Throwable e) {                                
                           }  

                           @Override  
                           public void onNext(RetrofitEntity retrofitEntity) {  
                               tvMsg.setText("無封裝:\n" + retrofitEntity.getData().toString());  
                           }                            
                       }  

               );複製代碼

簡單的RxJava集合Retrofit的使用就介紹完了,一樣的能夠發現使用起來不少重複性的代碼,並且使用也不是那麼簡單,因此纔有了下面的封裝


ReTrofit+Rxjava進階封裝之路

先來一張流程圖壓壓驚


請求數據封裝

1.參數

首先須要封裝的使咱們的數據類,在數據類中須要封裝請求中用到的相關數據的設置,好比請求參數、方法、加載框顯示設置等等

public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
    //rx生命週期管理
    private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
    /*回調*/
    private SoftReference<HttpOnNextListener> listener;
    /*是否能取消加載框*/
    private boolean cancel;
    /*是否顯示加載框*/
    private boolean showProgress;
    /*是否須要緩存處理*/
    private boolean cache;
    /*基礎url*/
    private  String baseUrl="http://www.izaodao.com/Api/";
    /*方法-若是須要緩存必須設置這個參數;不須要不用設置*/
    private String mothed;
    /*超時時間-默認6秒*/
    private int connectionTime = 6;
    /*有網狀況下的本地緩存時間默認60秒*/
    private int cookieNetWorkTime=60;
    /*無網絡的狀況下本地緩存時間默認30天*/
    private int cookieNoNetWorkTime=24*60*60*30;
}複製代碼

註釋很詳細,這裏不具體描述了,因爲這裏是最後封裝完成之後的代碼,因此有些內容本章還會部分不會涉及,由於功能太多,仍是按照一開始的博客章節講解。

2.抽象api接口

/** * 設置參數 * * @param retrofit * @return */
    public abstract Observable getObservable(Retrofit retrofit);複製代碼

經過子類也便是咱們的具體api接口,經過getObservable實現service中定義的接口方法,例如:

public class SubjectPostApi extends BaseApi {
     xxxxxxx
     xxxxxxx

 @Override
    public Observable getObservable(Retrofit retrofit) {
        HttpPostService service = retrofit.create(HttpPostService.class);
        return service.getAllVedioBys(isAll());
    }
}複製代碼

經過傳入的Retrofit對象,能夠隨意切換挑選Service對象,獲得定義的註解方法,初始完成之後返回Observable對象。

3.結果判斷

這裏結合RxJavamap方法在服務器返回數據中,統一處理數據處理,因此BaseApi<T> implements Func1<BaseResultEntity<T>, T>,後邊結合結果處理連接起來使用

@Override
    public T call(BaseResultEntity<T> httpResult) {
        if (httpResult.getRet() == 0) {
            throw new HttpTimeException(httpResult.getMsg());
        }
        return httpResult.getData();
    }複製代碼

因爲測試接口,也是當前咱們公司接口都是有統一規則的,想必你們都有這樣的接口規則,因此纔有這裏的統一判斷,規則以下:

* ret:1成功,2失敗  
 * msg:信息  
 * data:{  
 *       name:視頻名稱  
 *       title:標題  
 * }複製代碼

其實上面的接口文檔中就介紹了,統一先經過ret判斷,失敗顯示msg信息,data是成功後的數據也就是用戶關心的數據,因此可封裝一個結果對象BaseResultEntity.

4.結果數據

/** * 回調信息統一封裝類 * Created by WZG on 2016/7/16. */
public class BaseResultEntity<T> {
    // 判斷標示
    private int ret;
    // 提示信息
    private String msg;
    //顯示數據(用戶須要關心的數據)
    private T data;


    xxxxx  get-set  xxxxx
}複製代碼

這裏結合BaseApiFunc1判斷,失敗直接拋出一個異常,交個RxJavaonError處理,成功則將用戶關心的數據傳給Gson解析返回

5.泛型傳遞

BaseResultEntity<T>中的泛型T也就是咱們所關心的回調數據,一樣也是Gson最後解析返回的數據,傳遞的過程根節點是經過定義service方法是給定的,例如:

public interface HttpPostService {
    @POST("AppFiftyToneGraph/videoLink")
    Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
}複製代碼

其中的RetrofitEntity就是用戶關心的數據類,經過泛型傳遞給最後的接口。

6.強調

不少兄弟經過QQ羣反饋給我說,使用一個接口須要寫一個對應的api類繼承BaseApi是否是很麻煩,我這裏強調一下,這樣封裝是爲了將一個Api接口做爲一個對象去封裝,我的以爲有必要封裝成一個類,在往後工程日益增長接口隨着增長的同時,對象的作法更加有利於查找接口和修改接口有利於迭代。


操做類封裝

1初始對象

首先初始化一個單利方便HttpManager請求;這裏用了volatile的對象,不懂的同窗能夠參考個人另外一篇博客

你真的會寫單例嗎

private volatile static HttpManager INSTANCE;

    //構造方法私有
    private HttpManager() {
    }

    //獲取單例
    public static HttpManager getInstance() {
        if (INSTANCE == null) {
            synchronized (HttpManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new HttpManager();
                }
            }
        }
        return INSTANCE;
    }複製代碼

2接口處理和回調處理:

/** * 處理http請求 * * @param basePar 封裝的請求數據 */
    public void doHttpDeal(BaseApi basePar) {
        //手動建立一個OkHttpClient並設置超時時間緩存等設置
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
        builder.addInterceptor(new CookieInterceptor(basePar.isCache()));

        /*建立retrofit對象*/
        Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(basePar.getBaseUrl())
                .build();


        /*rx處理*/
        ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
        Observable observable = basePar.getObservable(retrofit)
                /*失敗後的retry配置*/
                .retryWhen(new RetryWhenNetworkException())
                /*生命週期管理*/
                .compose(basePar.getRxAppCompatActivity().bindToLifecycle())
                /*http請求線程*/
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                /*回調線程*/
                .observeOn(AndroidSchedulers.mainThread())
                /*結果判斷*/
                .map(basePar);

        /*數據回調*/
        observable.subscribe(subscriber);
    }複製代碼

首先經過api接口類BaseApi的實現類中數據初始化OkHttpClientRetrofit對象,其中包含了url,超時等,接着經過BaseApi的抽象方法getObservable獲得Observable對象,獲得Observable對象之後,咱們就能隨意的切換現成來處理,整個請求經過compose設定的rxlifecycle來管理生命週期,因此不會溢出和泄露無需任何擔憂,最後再服務器數據返回時,經過map判斷結果,剔除錯誤信息,成功之後返回到自定義的ProgressSubscriber對象中,因此接下來封裝ProgressSubscriber對象。


ProgressSubscriber封裝

ProgressSubscriber實際上是繼承於Subscriber,封裝的方法無非是對Subscriber的回調方法的封裝

  • onStart():開始
  • onCompleted():結束
  • onError(Throwable e):錯誤
  • onNext(T t):成功

1.請求加載框

http請求都伴隨着加載框的使用,因此這裏須要在onStart()使用前初始一個加載框,這裏簡單的用ProgressDialog代替

/** * 用於在Http請求開始時,自動顯示一個ProgressDialog * 在Http請求結束是,關閉ProgressDialog * 調用者本身對請求數據進行處理 * Created by WZG on 2016/7/16. */
public class ProgressSubscriber<T> extends Subscriber<T> {
    /*是否彈框*/
    private boolean showPorgress = true;
    /* 軟引用回調接口*/
    private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
    /*軟引用反正內存泄露*/
    private SoftReference<RxAppCompatActivity> mActivity;
    /*加載框可本身定義*/
    private ProgressDialog pd;
    /*請求數據*/
    private BaseApi api;


    /** * 構造 * * @param api */
    public ProgressSubscriber(BaseApi api) {
        this.api = api;
        this.mSubscriberOnNextListener = api.getListener();
        this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
        setShowPorgress(api.isShowProgress());
        if (api.isShowProgress()) {
            initProgressDialog(api.isCancel());
        }
    }


    /** * 初始化加載框 */
    private void initProgressDialog(boolean cancel) {
        Context context = mActivity.get();
        if (pd == null && context != null) {
            pd = new ProgressDialog(context);
            pd.setCancelable(cancel);
            if (cancel) {
                pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        onCancelProgress();
                    }
                });
            }
        }
    }


    /** * 顯示加載框 */
    private void showProgressDialog() {
        if (!isShowPorgress()) return;
        Context context = mActivity.get();
        if (pd == null || context == null) return;
        if (!pd.isShowing()) {
            pd.show();
        }
    }


    /** * 隱藏 */
    private void dismissProgressDialog() {
        if (!isShowPorgress()) return;
        if (pd != null && pd.isShowing()) {
            pd.dismiss();
        }
    }
}複製代碼

因爲progress的特殊性,須要指定content並且不能是Application因此這裏傳遞一個RxAppCompatActivity,而同時上面的HttpManager一樣須要,因此這裏統一仍是按照BaseApi傳遞過來,使用軟引用的方式避免泄露。剩下的無非是初始化,顯示和關閉方法,能夠詳細看代碼。

2.onStart()實現

onStart()中須要調用加載框,而後這裏還有網絡緩存的邏輯,後面會單獨講解,如今先忽略它的存在。

/** * 訂閱開始時調用 * 顯示ProgressDialog */
    @Override
    public void onStart() {
        showProgressDialog();
        /*緩存而且有網*/
        if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
             /*獲取緩存數據*/
            CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
            if (cookieResulte != null) {
                long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
                if (time < api.getCookieNetWorkTime()) {
                    if (mSubscriberOnNextListener.get() != null) {
                        mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
                    }
                    onCompleted();
                    unsubscribe();
                }
            }
        }
    }複製代碼

3.onCompleted()實現

/** * 完成,隱藏ProgressDialog */
    @Override
    public void onCompleted() {
        dismissProgressDialog();
    }複製代碼

4.onError(Throwable e)實現

onError(Throwable e)是對錯誤信息的處理和緩存讀取的處理,後續會講解,先忽略。

/** * 對錯誤進行統一處理 * 隱藏ProgressDialog * * @param e */
    @Override
    public void onError(Throwable e) {
        dismissProgressDialog();
        /*須要緩存而且本地有緩存才返回*/
        if (api.isCache()) {
            Observable.just(api.getUrl()).subscribe(new Subscriber<String>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {
                    errorDo(e);
                }

                @Override
                public void onNext(String s) {
                    /*獲取緩存數據*/
                    CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
                    if (cookieResulte == null) {
                        throw new HttpTimeException("網絡錯誤");
                    }
                    long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
                    if (time < api.getCookieNoNetWorkTime()) {
                        if (mSubscriberOnNextListener.get() != null) {
                            mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
                        }
                    } else {
                        CookieDbUtil.getInstance().deleteCookie(cookieResulte);
                        throw new HttpTimeException("網絡錯誤");
                    }
                }
            });
        } else {
            errorDo(e);
        }
    }

    /*錯誤統一處理*/
    private void errorDo(Throwable e) {
        Context context = mActivity.get();
        if (context == null) return;
        if (e instanceof SocketTimeoutException) {
            Toast.makeText(context, "網絡中斷,請檢查您的網絡狀態", Toast.LENGTH_SHORT).show();
        } else if (e instanceof ConnectException) {
            Toast.makeText(context, "網絡中斷,請檢查您的網絡狀態", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "錯誤" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
        if (mSubscriberOnNextListener.get() != null) {
            mSubscriberOnNextListener.get().onError(e);
        }
    }複製代碼

5.onNext(T t)實現

/** * 將onNext方法中的返回結果交給Activity或Fragment本身處理 * * @param t 建立Subscriber時的泛型類型 */
    @Override
    public void onNext(T t) {
        if (mSubscriberOnNextListener.get() != null) {
            mSubscriberOnNextListener.get().onNext(t);
        }
    }複製代碼

主要是是將獲得的結果,經過自定義的接口返回給view界面,其中的軟引用對象mSubscriberOnNextListener是自定義的接口回調類HttpOnNextListener.


6.HttpOnNextListener封裝

如今只需關心onNext(T t)onError(Throwable e)接口便可,回調的觸發點都是在上面的ProgressSubscriber中調用

/** * 成功回調處理 * Created by WZG on 2016/7/16. */
public abstract class HttpOnNextListener<T> {
    /** * 成功後回調方法 * @param t */
    public abstract void onNext(T t);

    /** * 緩存回調結果 * @param string */
    public void onCacheNext(String string){

    }

    /** * 失敗或者錯誤方法 * 主動調用,更加靈活 * @param e */
    public void onError(Throwable e){

    }

    /** * 取消回調 */
    public void onCancel(){

    }
}複製代碼

失敗後的retry處理

這裏你可能會問,Retrofit有自帶的retry處理呀,的確Retrofit有自帶的retry處理,可是有不少的侷限,先看下使用

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.retryOnConnectionFailure(true);複製代碼

使用起來仍是很方便,只須要調用一個方法便可,可是它是不可控的,也就是沒有辦法設置retry時間次數,因此不太靈活,既然如此還不如本身封裝一下,由於用RxJava實現這個簡直小菜,無形中好像已經給RxJava打了廣告,中毒太深。

很簡單直接上代碼:

/** * retry條件 * Created by WZG on 2016/10/17. */
public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
// retry次數
    private int count = 3;
// 延遲
    private long delay = 3000;
// 疊加延遲
    private long increaseDelay = 3000;

    public RetryWhenNetworkException() {

    }

    public RetryWhenNetworkException(int count, long delay) {
        this.count = count;
        this.delay = delay;
    }

    public RetryWhenNetworkException(int count, long delay, long increaseDelay) {
        this.count = count;
        this.delay = delay;
        this.increaseDelay = increaseDelay;
    }

    @Override
    public Observable<?> call(Observable<? extends Throwable> observable) {
        return observable
                .zipWith(Observable.range(1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
                    @Override
                    public Wrapper call(Throwable throwable, Integer integer) {
                        return new Wrapper(throwable, integer);
                    }
                }).flatMap(new Func1<Wrapper, Observable<?>>() {
                    @Override
                    public Observable<?> call(Wrapper wrapper) {
                        if ((wrapper.throwable instanceof ConnectException
                                || wrapper.throwable instanceof SocketTimeoutException
                                || wrapper.throwable instanceof TimeoutException)
                                && wrapper.index < count + 1) { //若是超出重試次數也拋出錯誤,不然默認是會進入onCompleted
                            return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);

                        }
                        return Observable.error(wrapper.throwable);
                    }
                });
    }

    private class Wrapper {
        private int index;
        private Throwable throwable;

        public Wrapper(Throwable throwable, int index) {
            this.index = index;
            this.throwable = throwable;
        }
    }
}複製代碼

使用

到這裏,咱們第一步封裝已經完成了,下面講解下如何使用,已經看明白的各位看官,估計早就看明白了使用方式,無非是建立一個api對象繼承BaseApi初始接口信息,而後調用HttpManager對象的doHttpDeal(BaseApi basePar)方法,最後靜靜的等待回調類HttpOnNextListener<T>類返回的onNext(T t)成功數據或者onError(Throwable e)數據。

其實代碼就是這樣:

api接口對象

/** * 測試數據 * Created by WZG on 2016/7/16. */
public class SubjectPostApi extends BaseApi {
// 接口須要傳入的參數 可自定義不一樣類型
    private boolean all;
    /*任何你先要傳遞的參數*/
// String xxxxx;

    /** * 默認初始化須要給定回調和rx週期類 * 能夠額外設置請求設置加載框顯示,回調等(可擴展) * @param listener * @param rxAppCompatActivity */
    public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
        super(listener,rxAppCompatActivity);
        setShowProgress(true);
        setCancel(true);
        setCache(true);
        setMothed("AppFiftyToneGraph/videoLink");
        setCookieNetWorkTime(60);
        setCookieNoNetWorkTime(24*60*60);
    }

    public boolean isAll() {
        return all;
    }

    public void setAll(boolean all) {
        this.all = all;
    }

    @Override
    public Observable getObservable(Retrofit retrofit) {
        HttpPostService service = retrofit.create(HttpPostService.class);
        return service.getAllVedioBys(isAll());
    }
}複製代碼

請求回調

// 完美封裝簡化版
    private void simpleDo() {
        SubjectPostApi postEntity = new SubjectPostApi(simpleOnNextListener,this);
        postEntity.setAll(true);
        HttpManager manager = HttpManager.getInstance();
        manager.doHttpDeal(postEntity);
    }

    // 回調一一對應
    HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<SubjectResulte>>() {
        @Override
        public void onNext(List<SubjectResulte> subjects) {
            tvMsg.setText("網絡返回:\n" + subjects.toString());
        }

        @Override
        public void onCacheNext(String cache) {
            /*緩存回調*/
            Gson gson=new Gson();
            java.lang.reflect.Type type = new TypeToken<BaseResultEntity<List<SubjectResulte>>>() {}.getType();
            BaseResultEntity resultEntity= gson.fromJson(cache, type);
            tvMsg.setText("緩存返回:\n"+resultEntity.getData().toString() );
        }

        /*用戶主動調用,默認是不須要覆寫該方法*/
        @Override
        public void onError(Throwable e) {
            super.onError(e);
            tvMsg.setText("失敗:\n" + e.toString());
        }

        /*用戶主動調用,默認是不須要覆寫該方法*/
        @Override
        public void onCancel() {
            super.onCancel();
            tvMsg.setText("取消請求");
        }
    };複製代碼

後續

到這裏,封裝功能中不少功能還沒涉及和講解,後續會陸續更新!
先給你們看看爲師的徹底體功能:

1.Retrofit+Rxjava+okhttp基本使用方法
    2.統一處理請求數據格式
    3.統一的ProgressDialog和回調Subscriber處理
    4.取消http請求
    5.預處理http請求
    6.返回數據的統一判斷
    7.失敗後的retry處理
    8.RxLifecycle管理生命週期,防止泄露
    9.文件上傳下載(支持多文件,斷點續傳)
    10.Cache數據持久化和數據庫(greenDao)兩種緩存機制
    11.異常統一處理複製代碼

來個圖壓壓驚:

火燒眉毛的小夥伴能夠看這裏:

RxJava+Retrofit+OkHttp深刻淺出-終極封裝

可是其中有些後續優化迭代未及時更新,別生氣,我立刻補!


源碼:

RxRetrofit-終極封裝-深刻淺出&網絡請求-GitHub

其實我還有一個兄弟版本-傳送門

我不會告訴你其實我還有個更加簡單的版本


建議

若是你對這套封裝有任何的問題和建議歡迎加入QQ羣告訴我


這篇文章參加掘金技術徵文:gold.xitu.io/post/58522d…

相關文章
相關標籤/搜索