RxJava+Retrofit+OkHttp深入淺出-終極封裝六特殊篇(變種String替換Gson自由擴充套件)

yangxi_001發表於2017-06-12

背景

在之前的封裝1-5Rxjava+ReTrofit+okHttp深入淺出-終極封裝中我們都是通過傳統的GsonConverterFactory自動解析,這樣做確實很方便,使用者能直接獲取返回的物件,不用關心具體的轉換,但是:這隨之而來有很多的缺陷(雖然官網推薦這樣使用);

比如:無法使用其他第三發轉換框架;泛型無法中間傳遞,封裝無法統一處理快取結果;回撥資訊無法統一處理;伺服器返回格式不嚴謹null解析異常………. 
所以我們在享受它遍歷的同時也被迫的要限制做很多的處理,限制我們的擴充套件!

本章就介紹如何放棄GsonConverterFactory,直接返回String,擴充套件我們的封裝!(封裝的整體思想和之前的封裝一樣,所以不會有大的改動!)

無須擔心,本篇封裝單獨作為一個專案和之前封裝分開,便於大家選擇!


效果

這裡寫圖片描述


功能

完全具備和之前封裝一樣的功能,這裡改用fastjson處理

    1.Retrofit+Rxjava+okhttp基本使用方法

    2.統一處理請求資料格式

    3.統一的ProgressDialog和回撥Subscriber處理

    4.取消http請求

    5.預處理http請求

    6.返回資料的統一判斷

    7.失敗後的retry處理

    8.RxLifecycle管理生命週期,防止洩露

    9.檔案上傳下載(支援多檔案,斷點續傳)

    10.Cache資料持久化和資料庫(greenDao)兩種快取機制

    11.一對多回撥介面處理
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

對比

話說沒有比較就沒有進步,所以大家比較下前後封裝的各自的優缺點,自行選擇合適自己的方案!

使用

Gson方案:

    //    完美封裝簡化版
    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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

String方案


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



    @Override
    public void onNext(String resulte, String mothead) {
        /*post返回處理*/
        if(mothead.equals(postEntity.getMothed())){
            List<SubjectResulte>  subjectResulte= JSONObject.parseArray(resulte,SubjectResulte.class);
            tvMsg.setText("post返回:\n"+subjectResulte.toString() );
        }

        /*上傳返回處理*/
        if(mothead.equals(uplaodApi.getMothed())){
            UploadResulte uploadResulte=JSONObject.parseObject(resulte,UploadResulte.class);
            tvMsg.setText("上傳成功返回:\n"+uploadResulte.getHeadImgUrl());
            Glide.with(MainActivity.this).load(uploadResulte.getHeadImgUrl()).skipMemoryCache(true).into(img);
        }
    }

    @Override
    public void onError(Throwable e) {
        tvMsg.setText("失敗:\n" + e.toString());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

Gson封裝方案中,我們採用了一一對應的返回原則,將所以的請求資料引數都放入到baseApi中,返回放入對應的HttpOnNextListener 中 
String方案中我們則採用一對多原則,將回撥和請求分開處理,公用一個回撥,通過回撥中的mothead來區分不同的介面,所以上述可以看見後者裡面其實還處理了上傳的回撥處理!

從封裝的用法上可以看出: 
優點:String封裝更加的靈活,可以指定Gson轉換的第三方工具,統一的結果返回處理程式碼更加的少(可以完美解決快取無法統一回撥的問題); 
同樣也有缺點:String封裝無法自動解析結果型別,需要手動處理(我反而覺得這也是它的優點,更加的靈活,個人看法)


實現

由於是基於之前的封裝修改,所以前提是瞭解之前的封裝以後才能完全瞭解一下的修改實現思路Rxjava+ReTrofit+okHttp深入淺出-終極封裝

1.替換GsonConverterFactory

由於GsonConverterFactory會自動解析Gson,替換成直接返回String的ScalarsConverterFactory 
匯入相關包(為了區別-使用fastjson可自由擴充套件)

    compile 'com.squareup.retrofit2:converter-scalars:+'
    compile 'com.alibaba:fastjson:+'
  • 1
  • 2
  • 1
  • 2

替換

 compile 'com.squareup.retrofit2:converter-gson:+'
 compile 'com.google.code.gson:gson:+'
  • 1
  • 2
  • 1
  • 2

2.修改retrofit構建

ScalarsConverterFactory替換GsonConverterFactory

  /*建立retrofit物件*/
        Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(ScalarsConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(basePar.getBaseUrl())
                .build();
        HttpService  httpService = retrofit.create(HttpService.class);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

替換

    /*建立retrofit物件*/
        Retrofit retrofit = new Retrofit.Builder()
                .client(builder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(basePar.getBaseUrl())
                .build();
        HttpService  httpService = retrofit.create(HttpService.class);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.修改快取記錄位置

由於之前是為了防止gson重複解析,將快取放入到自定義CookieInterceptor中;既然現在不需要自動轉換,直接返回String,所以直接將快取資料處理放入到ProgressSubscriber的onNext中處理;

修改1:去掉CookieInterceptor

     //手動建立一個OkHttpClient並設定超時時間快取等設定
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addInterceptor(new CacheInterceptor());
        builder.addNetworkInterceptor(new CacheInterceptor());
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

替換

  //手動建立一個OkHttpClient並設定超時時間快取等設定
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addNetworkInterceptor(new CacheInterceptor());
        builder.addInterceptor(new CookieInterceptor(basePar.isCache()));
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

修改二:實現快取處理 
在onNext中實現快取處理

    /**
     * 將onNext方法中的返回結果交給Activity或Fragment自己處理
     *
     * @param t 建立Subscriber時的泛型型別
     */
    @Override
    public void onNext(T t) {
         /*快取處理*/
        if(api.isCache()){
            CookieResulte resulte= CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
            long time=System.currentTimeMillis();
            /*儲存和更新本地資料*/
            if(resulte==null){
                resulte  =new CookieResulte(api.getUrl(),t.toString(),time);
                CookieDbUtil.getInstance().saveCookie(resulte);
            }else{
                resulte.setResulte(t.toString());
                resulte.setTime(time);
                CookieDbUtil.getInstance().updateCookie(resulte);
            }
        }
        if (mSubscriberOnNextListener.get() != null) {
            mSubscriberOnNextListener.get().onNext((String) t,api.getMothed());
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

4.修改回撥介面資訊

由於現在通過String直接返回,所以可以將成功回撥和快取回撥合並處理;另一方面沒有了泛型的限制,在回撥時可以通過介面請求引數實現一對多回撥處理;

/**
 * 成功回撥處理
 * Created by WZG on 2016/7/16.
 */
public interface  HttpOnNextListener {
    /**
     * 成功後回撥方法
     * @param resulte
     * @param method
     */
   void onNext(String resulte,String method);

    /**
     * 失敗或者錯誤方法
     * 主動呼叫,更加靈活
     * @param e
     */
   void onError(Throwable e);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

5.修改BaseApi

由於取消了泛型返回的機制,所以在Func1判斷時需要手動轉換資料;這裡示例fastjeson用法轉換

 @Override
    public String call(T httpResult) {
        BaseResultEntity baseResulte= JSONObject.parseObject(httpResult.toString(),BaseResultEntity.class);
        if (baseResulte.getRet() == 0) {
            throw new HttpTimeException(baseResulte.getMsg());
        }
        return baseResulte.getData();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

替換

    @Override
    public T call(BaseResultEntity<T> httpResult) {
        if (httpResult.getRet() == 0) {
            throw new HttpTimeException(httpResult.getMsg());
        }
        return httpResult.getData();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

6.修改結果基礎類BaseResultEntity

將泛型資料改成String資料型別

/**
 * 回撥資訊統一封裝類
 * Created by WZG on 2016/7/16.
 */
public class BaseResultEntity {
    //  判斷標示
    private int ret;
    //    提示資訊
    private String msg;
    //顯示資料(使用者需要關心的資料)
    private String data;
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

替換

/**
 * 回撥資訊統一封裝類
 * Created by WZG on 2016/7/16.
 */
public class BaseResultEntity<T> {
    //  判斷標示
    private int ret;
    //    提示資訊
    private String msg;
    //顯示資料(使用者需要關心的資料)
    private T data;
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

7.合併快取和成功回到返回處理

由於取消泛型,快取和成統一處理所以需要修改


    /**
     * 訂閱開始時呼叫
     * 顯示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
        /*快取並且有網*/
        if(api.isCache()&& AppUtil.isNetworkAvailable(MyApplication.app)){
             /*獲取快取資料*/
            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().onNext(cookieResulte.getResulte(),api.getMothed());
                    }
                    onCompleted();
                    unsubscribe();
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

替換


    /**
     * 訂閱開始時呼叫
     * 顯示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
        /*快取並且有網*/
        if(api.isCache()&& AppUtil.isNetworkAvailable(MyApplication.app)){
             /*獲取快取資料*/
            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();
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

8.修改一對多回撥處理

沒有了泛型,可以修改HttpManager,採用動態建立,動態回撥的方法解決多巢狀耦合的問題 
修改1:去掉預設構造傳參

 public BaseApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
        setListener(listener);
        setRxAppCompatActivity(rxAppCompatActivity);
        setShowProgress(true);
        setCache(true);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

修改2:新增HttpManager動態傳參

/**
 * http互動處理類
 * Created by WZG on 2016/7/16.
 */
public class HttpManager {
    /*弱引用物件*/
    private SoftReference<HttpOnNextListener>  onNextListener;
    private SoftReference<RxAppCompatActivity> appCompatActivity;

    public HttpManager(HttpOnNextListener onNextListener, RxAppCompatActivity appCompatActivity) {
        this.onNextListener=new SoftReference(onNextListener);
        this.appCompatActivity=new SoftReference(appCompatActivity);
    }
    *******************
    *******************
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

修改3:通過method動態判斷介面返回

public class MainActivity extends RxAppCompatActivity implements HttpOnNextListener{
    @Override
    public void onNext(String resulte, String method) {
        /*post返回處理*/
        if(method.equals(postEntity.getMothed())){
           *******
        }

        /*上傳返回處理*/
        if(method.equals(uplaodApi.getMothed())){
           *********
        }
    }

    @Override
    public void onError(Throwable e) {
        tvMsg.setText("失敗:\n" + e.toString());
    }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

大功告成!


下載模組

由於下載模組是獨立存在,所以基本沒有修改,唯一修改的地方就是將HttpDownManager中的GsonConverterFactory替換成ScalarsConverterFactory即可!


總結

通過自定義String型別的返回處理方式,有效的解決了之前Gson自動轉換的問題

  • 1.一對一返回問題(程式碼量多)

  • 2.快取回撥無法和成功統一處理

  • 3.無法指定gson轉換第三方庫

  • 4.回撥監聽的多巢狀(耦合度大)

  • 5.解決伺服器資料null異常

注意:這裡只是給大家提供了一個不同的解決方案,Gson自動解析返回的方案也是有它的優點,可以大大的減少開發的工作量,優缺點也很明顯;孰好孰壞自行判斷,自行選擇適合自己的方案(個人偏向後者String返回,比較靈活)


終極封裝專欄

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


原始碼

原始碼傳送門-String變種方案-Github

原始碼傳送門-Gson方案-Github


建議

如果你對這套封裝有任何的問題和建議歡迎加入QQ群告訴我!

相關文章