<轉(zhuǎn)>RxJava+Retrofit+OkHttp深入淺出-終極封裝二(網(wǎng)絡(luò)請(qǐng)求)

封裝成果

封裝完以后驼鞭,具有如下功能:

    1.Retrofit+Rxjava+okhttp基本使用方法
    2.統(tǒng)一處理請(qǐng)求數(shù)據(jù)格式
    3.統(tǒng)一的ProgressDialog和回調(diào)Subscriber處理
    4.取消http請(qǐng)求
    5.預(yù)處理http請(qǐng)求
    6.返回?cái)?shù)據(jù)的統(tǒng)一判斷
    7.失敗后的retry封裝處理
    8.RxLifecycle管理生命周期皮获,防止泄露

效果:

ebcee353719368ac7f7a7c3c93cad854.gif
具體使用:

封裝后http請(qǐng)求代碼如下

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

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

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

是不是很簡(jiǎn)單初家?你可能說(shuō)這還簡(jiǎn)單,好咱們對(duì)比一下正常使用Retrofit的方法

/**  
    * Retrofit加入rxjava實(shí)現(xiàn)http請(qǐng)求  
    */  
   private void onButton9Click() {  
       //手動(dòng)創(chuàng)建一個(gè)OkHttpClient并設(shè)置超時(shí)時(shí)間  
       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("無(wú)封裝:\n" + retrofitEntity.getData().toString());  
                           }  

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

               );  
   }

可能你發(fā)現(xiàn)確是代碼有點(diǎn)多乌助,但是更加可怕的是溜在,如果你一個(gè)activity或者fragment中多次需要http請(qǐng)求,你需要多次重復(fù)的寫回調(diào)處理(一個(gè)回到就有4個(gè)方法呀K小R蠢摺!赏参!反正我是忍受不了)志笼,而且以上處理還沒(méi)有做過(guò)多的判斷和錯(cuò)誤校驗(yàn)就如此復(fù)雜沿盅!~好了介紹完了,開始咱們的優(yōu)化之路吧籽腕!


項(xiàng)目結(jié)構(gòu):
03184db20dc2b7b159d4.png

RxJava

如果你對(duì)RxJava不了解嗡呼,好吧騷年趕快學(xué)學(xué)吧,不然真會(huì)out了,下面給出博主當(dāng)初學(xué)習(xí)RxJava的一些資源:

Retrofit

咱家今天的主角來(lái)了皇耗,咱們也深入淺出一下了解下Retrofit使用南窗,前方高能,如果你是深度Retrofit選手請(qǐng)直接跳過(guò)本節(jié)@陕ァM蛏恕!

1.首先確保在AndroidManifest.xml中請(qǐng)求了網(wǎng)絡(luò)權(quán)限

<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請(qǐng)求傳遞參數(shù)
  • @Field:用于Post方式傳遞參數(shù),需要在請(qǐng)求接口方法上添加@FormUrlEncoded,即以表單的方式傳遞參數(shù)
  • @Body:用于Post,根據(jù)轉(zhuǎn)換方式將實(shí)例對(duì)象轉(zhuǎn)化為對(duì)應(yīng)字符串傳遞參數(shù).比如Retrofit添加GsonConverterFactory則是將body轉(zhuǎn)化為gson字符串進(jìn)行傳遞
  • @Path:用于URL上占位符
  • @Part:配合@Multipart使用,一般用于文件上傳
  • @Header:添加http header
  • @Headers:跟@Header作用一樣,只是使用方式不一樣,@Header是作為請(qǐng)求方法的參數(shù)傳入,@Headers是以固定方式直接添加到請(qǐng)求方法上
ReTrofit基本使用:

首先給定一個(gè)測(cè)試接口文檔敌买,后面的博客中我們都是用這個(gè)接口調(diào)試

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

要向一個(gè)api發(fā)送我們的網(wǎng)絡(luò)請(qǐng)求 ,我們需要使用Retrofit builder類并指定service的base URL(通常情況下就是域名)阶界。

String BASE_URL = " http://www.izaodao.com/Api/"  
    Retrofit retrofit = new Retrofit.Builder()  
            .baseUrl(BASE_URL)  
            .addConverterFactory(GsonConverterFactory.create())  
            .build();
2.設(shè)置接口service

注意到每個(gè)endpoint都指定了一個(gè)關(guān)于HTTP(GET, POST, 等等虹钮。) 方法的注解以及用于分發(fā)網(wǎng)絡(luò)調(diào)用的方法。而且這些方法的參數(shù)也可以有特殊的注解膘融。

/**  
 * 接口地址  
 * Created by WZG on 2016/7/16.  
 */  
public interface MyApiEndpointInterface {  
    @POST("AppFiftyToneGraph/videoLink")  
    Call<RetrofitEntity> getAllVedio(@Body boolean once_no)             
}
3.得到call然后同步處理處理回調(diào):
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());  

    }  
});

這就是簡(jiǎn)單的Retrofit使用步驟芙粱,接下來(lái)我們結(jié)合RxJava講述

ReTrofit+Rxjava基本使用

對(duì)比之前的Retrofit使用

1.在于我們需要修改service接口返回信息我們需要返回一個(gè)Observable對(duì)象
@POST("AppFiftyToneGraph/videoLink")  
Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);
2.然后初始化Retrofit需要添加對(duì)Rxjava的適配,注意一定要retrofit2才有這個(gè)功能哦
Retrofit retrofit = new Retrofit.Builder()  
                .client(builder.build())  
                .addConverterFactory(GsonConverterFactory.create())  
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())  
                .baseUrl(HttpManager.BASE_URL)  
                .build();
3.回調(diào)通過(guò)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("無(wú)封裝:\n" + retrofitEntity.getData().toString());  
                           }                            
                       }  

               );

簡(jiǎn)單的RxJava集合Retrofit的使用就介紹完了氧映,同樣的可以發(fā)現(xiàn)使用起來(lái)很多重復(fù)性的代碼春畔,而且使用也不是那么簡(jiǎn)單,所以才有了下面的封裝

ReTrofit+Rxjava進(jìn)階封裝之路

先來(lái)一張流程圖壓壓驚

17c55b03c9b9d8e99ff07dbe611f285b.png
請(qǐng)求數(shù)據(jù)封裝
1.參數(shù)

首先需要封裝的使我們的數(shù)據(jù)類岛都,在數(shù)據(jù)類中需要封裝請(qǐng)求中用到的相關(guān)數(shù)據(jù)的設(shè)置律姨,比如請(qǐng)求參數(shù)、方法臼疫、加載框顯示設(shè)置等等

public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
    //rx生命周期管理
    private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
    /*回調(diào)*/
    private SoftReference<HttpOnNextListener> listener;
    /*是否能取消加載框*/
    private boolean cancel;
    /*是否顯示加載框*/
    private boolean showProgress;
    /*是否需要緩存處理*/
    private boolean cache;
    /*基礎(chǔ)url*/
    private  String baseUrl="http://www.izaodao.com/Api/";
    /*方法-如果需要緩存必須設(shè)置這個(gè)參數(shù)择份;不需要不用設(shè)置*/
    private String mothed;
    /*超時(shí)時(shí)間-默認(rèn)6秒*/
    private int connectionTime = 6;
    /*有網(wǎng)情況下的本地緩存時(shí)間默認(rèn)60秒*/
    private int cookieNetWorkTime=60;
    /*無(wú)網(wǎng)絡(luò)的情況下本地緩存時(shí)間默認(rèn)30天*/
    private int cookieNoNetWorkTime=24*60*60*30;
}

注釋很詳細(xì),這里不具體描述了烫堤,由于這里是最后封裝完成以后的代碼缓淹,所以有些內(nèi)容本章還會(huì)部分不會(huì)涉及,因?yàn)楣δ芴嗨樱€是按照一開始的博客章節(jié)講解。

2.抽象api接口
/**
     * 設(shè)置參數(shù)
     *
     * @param retrofit
     * @return
     */
    public abstract Observable getObservable(Retrofit retrofit);

通過(guò)子類也即是我們的具體api接口料仗,通過(guò)getObservable實(shí)現(xiàn)service中定義的接口方法湾盗,例如:

public class SubjectPostApi extends BaseApi {
     xxxxxxx
     xxxxxxx

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

通過(guò)傳入的Retrofit對(duì)象,可以隨意切換挑選Service對(duì)象立轧,得到定義的注解方法格粪,初始完成以后返回Observable對(duì)象躏吊。

3.結(jié)果判斷

這里結(jié)合RxJava的map方法在服務(wù)器返回?cái)?shù)據(jù)中,統(tǒng)一處理數(shù)據(jù)處理帐萎,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>比伏,后邊結(jié)合結(jié)果處理鏈接起來(lái)使用

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

由于測(cè)試接口,也是當(dāng)前我們公司接口都是有統(tǒng)一規(guī)則的疆导,想必大家都有這樣的接口規(guī)則赁项,所以才有這里的統(tǒng)一判斷,規(guī)則如下:

* ret:1成功,2失敗  
 * msg:信息  
 * data:{  
 *       name:視頻名稱  
 *       title:標(biāo)題  
 * }

其實(shí)上面的接口文檔中就介紹了,統(tǒng)一先通過(guò)ret判斷澈段,失敗顯示msg信息,data是成功后的數(shù)據(jù)也就是用戶關(guān)心的數(shù)據(jù)悠菜,所以可封裝一個(gè)結(jié)果對(duì)象BaseResultEntity.

4.結(jié)果數(shù)據(jù)
/**
 * 回調(diào)信息統(tǒng)一封裝類
 * Created by WZG on 2016/7/16.
 */
public class BaseResultEntity<T> {
    //  判斷標(biāo)示
    private int ret;
    //    提示信息
    private String msg;
    //顯示數(shù)據(jù)(用戶需要關(guān)心的數(shù)據(jù))
    private T data;


    xxxxx  get-set  xxxxx
}

這里結(jié)合BaseApi的Func1判斷,失敗直接拋出一個(gè)異常败富,交個(gè)RxJava的onError處理悔醋,成功則將用戶關(guān)心的數(shù)據(jù)傳給Gson解析返回

5.泛型傳遞

BaseResultEntity<T>中的泛型T也就是我們所關(guān)心的回調(diào)數(shù)據(jù),同樣也是Gson最后解析返回的數(shù)據(jù)兽叮,傳遞的過(guò)程根節(jié)點(diǎn)是通過(guò)定義service方法是給定的芬骄,例如:

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

其中的RetrofitEntity就是用戶關(guān)心的數(shù)據(jù)類,通過(guò)泛型傳遞給最后的接口鹦聪。

6.強(qiáng)調(diào)

很多兄弟通過(guò)QQ群反饋給我說(shuō)账阻,使用一個(gè)接口需要寫一個(gè)對(duì)應(yīng)的api類繼承BaseApi是不是很麻煩,我這里強(qiáng)調(diào)一下椎麦,這樣封裝是為了將一個(gè)Api接口作為一個(gè)對(duì)象去封裝宰僧,個(gè)人覺(jué)得有必要封裝成一個(gè)類,在日后工程日益增加接口隨著增加的同時(shí)观挎,對(duì)象的做法更加有利于查找接口和修改接口有利于迭代琴儿。


操作類封裝
1初始對(duì)象

首先初始化一個(gè)單利方便HttpManager請(qǐng)求;這里用了volatile的對(duì)象嘁捷,不懂的同學(xué)可以參考我的另一篇博客

你真的會(huì)寫單例嗎造成?

  private volatile static HttpManager INSTANCE;

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

    //獲取單例
    public static HttpManager getInstance() {
        if (INSTANCE == null) {
            synchronized (HttpManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new HttpManager();
                }
            }
        }
        return INSTANCE;
    }
2接口處理和回調(diào)處理:
 /**
     * 處理http請(qǐng)求
     *
     * @param basePar 封裝的請(qǐng)求數(shù)據(jù)
     */
    public void doHttpDeal(BaseApi basePar) {
        //手動(dòng)創(chuàng)建一個(gè)OkHttpClient并設(shè)置超時(shí)時(shí)間緩存等設(shè)置
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
        builder.addInterceptor(new CookieInterceptor(basePar.isCache()));

        /*創(chuàng)建retrofit對(duì)象*/
        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請(qǐng)求線程*/
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                /*回調(diào)線程*/
                .observeOn(AndroidSchedulers.mainThread())
                /*結(jié)果判斷*/
                .map(basePar);

        /*數(shù)據(jù)回調(diào)*/
        observable.subscribe(subscriber);
    }

首先通過(guò)api接口類BaseApi的實(shí)現(xiàn)類中數(shù)據(jù)初始化OkHttpClient和Retrofit對(duì)象,其中包含了url雄嚣,超時(shí)等晒屎,接著通過(guò)BaseApi的抽象方法getObservable得到Observable對(duì)象,得到Observable對(duì)象以后缓升,我們就能隨意的切換現(xiàn)成來(lái)處理鼓鲁,整個(gè)請(qǐng)求通過(guò)compose設(shè)定的rxlifecycle來(lái)管理生命周期,所以不會(huì)溢出和泄露無(wú)需任何擔(dān)心港谊,最后再服務(wù)器數(shù)據(jù)返回時(shí)骇吭,通過(guò)map判斷結(jié)果,剔除錯(cuò)誤信息歧寺,成功以后返回到自定義的ProgressSubscriber對(duì)象中燥狰,所以接下來(lái)封裝ProgressSubscriber對(duì)象棘脐。

ProgressSubscriber封裝

  • onStart():開始
  • onCompleted():結(jié)束
  • onError(Throwable e):錯(cuò)誤
  • onNext(T t):成功
1.請(qǐng)求加載框

http請(qǐng)求都伴隨著加載框的使用,所以這里需要在onStart()使用前初始一個(gè)加載框龙致,這里簡(jiǎn)單的用ProgressDialog代替

/**
 * 用于在Http請(qǐng)求開始時(shí)蛀缝,自動(dòng)顯示一個(gè)ProgressDialog
 * 在Http請(qǐng)求結(jié)束是,關(guān)閉ProgressDialog
 * 調(diào)用者自己對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行處理
 * Created by WZG on 2016/7/16.
 */
public class ProgressSubscriber<T> extends Subscriber<T> {
    /*是否彈框*/
    private boolean showPorgress = true;
    /* 軟引用回調(diào)接口*/
    private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
    /*軟引用反正內(nèi)存泄露*/
    private SoftReference<RxAppCompatActivity> mActivity;
    /*加載框可自己定義*/
    private ProgressDialog pd;
    /*請(qǐng)求數(shù)據(jù)*/
    private BaseApi api;


    /**
     * 構(gòu)造
     *
     * @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所以這里傳遞一個(gè)RxAppCompatActivity,而同時(shí)上面的HttpManager同樣需要屈梁,所以這里統(tǒng)一還是按照BaseApi傳遞過(guò)來(lái),使用軟引用的方式避免泄露像啼。剩下的無(wú)非是初始化俘闯,顯示和關(guān)閉方法,可以詳細(xì)看代碼忽冻。

2.onStart()實(shí)現(xiàn)

在onStart()中需要調(diào)用加載框真朗,然后這里還有網(wǎng)絡(luò)緩存的邏輯,后面會(huì)單獨(dú)講解僧诚,現(xiàn)在先忽略它的存在遮婶。

 /**
     * 訂閱開始時(shí)調(diào)用
     * 顯示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
        /*緩存并且有網(wǎng)*/
        if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
             /*獲取緩存數(shù)據(jù)*/
            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()實(shí)現(xiàn)
 /**
     * 完成,隱藏ProgressDialog
     */
    @Override
    public void onCompleted() {
        dismissProgressDialog();
    }
4.onError(Throwable e)實(shí)現(xiàn)

在onError(Throwable e)是對(duì)錯(cuò)誤信息的處理和緩存讀取的處理湖笨,后續(xù)會(huì)講解旗扑,先忽略。

/**
     * 對(duì)錯(cuò)誤進(jìn)行統(tǒng)一處理
     * 隱藏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) {
                    /*獲取緩存數(shù)據(jù)*/
                    CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
                    if (cookieResulte == null) {
                        throw new HttpTimeException("網(wǎng)絡(luò)錯(cuò)誤");
                    }
                    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("網(wǎng)絡(luò)錯(cuò)誤");
                    }
                }
            });
        } else {
            errorDo(e);
        }
    }

    /*錯(cuò)誤統(tǒng)一處理*/
    private void errorDo(Throwable e) {
        Context context = mActivity.get();
        if (context == null) return;
        if (e instanceof SocketTimeoutException) {
            Toast.makeText(context, "網(wǎng)絡(luò)中斷慈省,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)", Toast.LENGTH_SHORT).show();
        } else if (e instanceof ConnectException) {
            Toast.makeText(context, "網(wǎng)絡(luò)中斷臀防,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "錯(cuò)誤" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
        if (mSubscriberOnNextListener.get() != null) {
            mSubscriberOnNextListener.get().onError(e);
        }
    }
5.onNext(T t)實(shí)現(xiàn)
  /**
     * 將onNext方法中的返回結(jié)果交給Activity或Fragment自己處理
     *
     * @param t 創(chuàng)建Subscriber時(shí)的泛型類型
     */
    @Override
    public void onNext(T t) {
        if (mSubscriberOnNextListener.get() != null) {
            mSubscriberOnNextListener.get().onNext(t);
        }
    }

主要是是將得到的結(jié)果,通過(guò)自定義的接口返回給view界面边败,其中的軟引用對(duì)象mSubscriberOnNextListener是自定義的接口回調(diào)類HttpOnNextListener.

6.HttpOnNextListener封裝

現(xiàn)在只需關(guān)心onNext(T t)onError(Throwable e)接口即可袱衷,回調(diào)的觸發(fā)點(diǎn)都是在上面的ProgressSubscriber中調(diào)用

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

    /**
     * 緩存回調(diào)結(jié)果
     * @param string
     */
    public void onCacheNext(String string){

    }

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

    }

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

    }
}

失敗后的retry處理

這里你可能會(huì)問(wèn)笑窜,Retrofit有自帶的retry處理呀致燥,的確Retrofit有自帶的retry處理,但是有很多的局限排截,先看下使用

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.retryOnConnectionFailure(true);

使用起來(lái)還是很方便嫌蚤,只需要調(diào)用一個(gè)方法即可,但是它是不可控的断傲,也就是沒(méi)有辦法設(shè)置retry時(shí)間次數(shù)脱吱,所以不太靈活,既然如此還不如自己封裝一下认罩,因?yàn)橛肦xJava實(shí)現(xiàn)這個(gè)簡(jiǎn)直小菜箱蝠,無(wú)形中好像已經(jīng)給RxJava打了廣告,中毒太深。
很簡(jiǎn)單直接上代碼:

/**
 * retry條件
 * Created by WZG on 2016/10/17.
 */
public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
//    retry次數(shù)
    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) { //如果超出重試次數(shù)也拋出錯(cuò)誤抡锈,否則默認(rèn)是會(huì)進(jìn)入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;
        }
    }
}

使用

到這里,我們第一步封裝已經(jīng)完成了乔外,下面講解下如何使用床三,已經(jīng)看明白的各位看官,估計(jì)早就看明白了使用方式杨幼,無(wú)非是創(chuàng)建一個(gè)api對(duì)象繼承BaseApi初始接口信息撇簿,然后調(diào)用HttpManager對(duì)象的doHttpDeal(BaseApi basePar)方法,最后靜靜的等待回調(diào)類HttpOnNextListener<T>類返回的onNext(T t)成功數(shù)據(jù)或者onError(Throwable e)數(shù)據(jù)差购。
其實(shí)代碼就是這樣:

api接口對(duì)象
/**
 * 測(cè)試數(shù)據(jù)
 * Created by WZG on 2016/7/16.
 */
public class SubjectPostApi extends BaseApi {
//    接口需要傳入的參數(shù) 可自定義不同類型
    private boolean all;
    /*任何你先要傳遞的參數(shù)*/
//    String xxxxx;

    /**
     * 默認(rèn)初始化需要給定回調(diào)和rx周期類
     * 可以額外設(shè)置請(qǐng)求設(shè)置加載框顯示四瘫,回調(diào)等(可擴(kuò)展)
     * @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());
    }
}
請(qǐng)求回調(diào)

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

    //   回調(diào)一一對(duì)應(yīng)
    HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<SubjectResulte>>() {
        @Override
        public void onNext(List<SubjectResulte> subjects) {
            tvMsg.setText("網(wǎng)絡(luò)返回:\n" + subjects.toString());
        }

        @Override
        public void onCacheNext(String cache) {
            /*緩存回調(diào)*/
            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() );
        }

        /*用戶主動(dòng)調(diào)用,默認(rèn)是不需要覆寫該方法*/
        @Override
        public void onError(Throwable e) {
            super.onError(e);
            tvMsg.setText("失斢印:\n" + e.toString());
        }

        /*用戶主動(dòng)調(diào)用找蜜,默認(rèn)是不需要覆寫該方法*/
        @Override
        public void onCancel() {
            super.onCancel();
            tvMsg.setText("取消請(qǐng)求");
        }
    };

后續(xù)

到這里,封裝功能中很多功能還沒(méi)涉及和講解稳析,后續(xù)會(huì)陸續(xù)更新洗做!
先給大家看看為師的完全體功能:

    1.Retrofit+Rxjava+okhttp基本使用方法
    2.統(tǒng)一處理請(qǐng)求數(shù)據(jù)格式
    3.統(tǒng)一的ProgressDialog和回調(diào)Subscriber處理
    4.取消http請(qǐng)求
    5.預(yù)處理http請(qǐng)求
    6.返回?cái)?shù)據(jù)的統(tǒng)一判斷
    7.失敗后的retry處理
    8.RxLifecycle管理生命周期,防止泄露
    9.文件上傳下載(支持多文件彰居,斷點(diǎn)續(xù)傳)
    10.Cache數(shù)據(jù)持久化和數(shù)據(jù)庫(kù)(greenDao)兩種緩存機(jī)制
    11.異常統(tǒng)一處理

來(lái)個(gè)圖壓壓驚:

392c43f0540bbac7f2fc093566aed53d.gif
迫不及待的小伙伴可以看這里:

RxJava+Retrofit+OkHttp深入淺出-終極封裝專欄


源碼:

RxRetrofit-終極封裝-深入淺出&網(wǎng)絡(luò)請(qǐng)求-GitHub
其實(shí)我還有一個(gè)兄弟版本-傳送門
我會(huì)告訴你其實(shí)我還有個(gè)更加簡(jiǎn)單的版本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诚纸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子陈惰,更是在濱河造成了極大的恐慌畦徘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抬闯,死亡現(xiàn)場(chǎng)離奇詭異井辆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)画髓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門掘剪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人奈虾,你說(shuō)我怎么就攤上這事夺谁。” “怎么了肉微?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵匾鸥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碉纳,道長(zhǎng)勿负,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮奴愉,結(jié)果婚禮上琅摩,老公的妹妹穿的比我還像新娘。我一直安慰自己锭硼,他們只是感情好房资,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著檀头,像睡著了一般轰异。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上暑始,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天搭独,我揣著相機(jī)與錄音,去河邊找鬼廊镜。 笑死牙肝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的期升。 我是一名探鬼主播惊奇,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼播赁!你這毒婦竟也來(lái)了颂郎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤容为,失蹤者是張志新(化名)和其女友劉穎乓序,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坎背,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡替劈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了得滤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陨献。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖懂更,靈堂內(nèi)的尸體忽然破棺而出眨业,到底是詐尸還是另有隱情,我是刑警寧澤沮协,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布龄捡,位于F島的核電站,受9級(jí)特大地震影響慷暂,放射性物質(zhì)發(fā)生泄漏聘殖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奸腺。 院中可真熱鬧餐禁,春花似錦、人聲如沸突照。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)绷旗。三九已至,卻和暖如春副砍,著一層夾襖步出監(jiān)牢的瞬間衔肢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工豁翎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留角骤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓心剥,卻偏偏與公主長(zhǎng)得像邦尊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子优烧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容