1. 程式人生 > >Retrofit2+Rxjava2聯網的封裝

Retrofit2+Rxjava2聯網的封裝

前言

開發過程中,一般都會對網路框架進行再次封裝,以配置各種引數,並適合自己的編碼風格。各種網路框架比較下來,還是Retrofit2+Rxjava2看著最爽,今天把這個東西整理了一下,發出來,示例給出了一般寫法和MVP的寫法。

Retrofit2+Rxjava2

Retrofit2Rxjava2基礎的東西就不說了,直接進入主題。我們的需求是:

1、儘可能簡潔
2、可控制不同請求的載入框,
3、錯誤統一處理
4、頁面銷燬時取消訂閱
5、可根據不同請求處理不同的異常

先看一下最終的效果:

  Api.getDefaultService()
                .calendarBean("2017-06-29"
) .map(new RxFunction<Calendar>()) .compose(RxSchedulers.<Calendar>io_main()) .subscribe(new RxObserver<Calendar>(this, TAG, 0, false) { @Override public void onSuccess(int whichRequest, Calendar calendar) { tv_.setText(calendar.getLunar()); } @Override
public void onError(int whichRequest, Throwable e) { } });

這個例子其中就包含了以上5點要求,其中大多是對RxObserver的處理。RxObserver實現Observer介面,在Rxjava2中作為觀察者,在執行onNext之前先做一些處理,就能相應地減少在View層的處理。


    public RxObserver(Context context, String key, int whichRequest, boolean
isShowDialog) { this.mContext = context; this.mKey = key; this.isShowDialog = isShowDialog; this.mWhichRequest = whichRequest; mDialog = new ProgressDialog(context); mDialog.setTitle("請稍後"); mRxManager = RxManager.getInstance(); }

引數說明:
keykey是用來區分不同類中聯網的CompositeDisposable的,以便在這個類銷燬時,可以取消該類中訂閱關係,建議採用包名+類名作為key,這個後面會詳細說。
whichRequest:區分不同的請求,用於多個聯網請求結束後對不同請求的處理。
isShowDialog:是否顯示載入框,RxObserver內部例項化了一個載入框,可根據需求設定是否顯示。

RxObserver實現了Observer的四個方法 void onSubscribe(@NonNull Disposable d);void onNext(@NonNull T t);void onError(@NonNull Throwable e);void onComplete();

    @Override
    public final void onSubscribe(Disposable d) {
        mRxManager.add(mKey, d);
        if (isShowDialog) {
            mDialog.show();
        }
        onStart(mWhichRequest);
    }

onSubscribe(Disposable d)方法,相當於Rxjava1中的onStar()方法,其中的引數是Disposable ,用於取消該訂閱關係,所以在方法中把它新增進了RxManager中,以方便取消訂閱。同時也判斷了isShowDialog了是否顯示載入框,添加了一個方法onStart()方法,同時把mWhichRequest傳出去方便在外部回撥。

 @Override
    public final void onNext(T value) {
        onSuccess(mWhichRequest, value);
    }

onNext(T value)方法比較簡單,聯網結果成功返回會執行,引數是結果。在這個方法中寫了抽象方法onSuccess(mWhichRequest, value);同樣把mWhichRequest傳出,方便處理。

@Override
    public final void onComplete() {
        if (mDialog.isShowing()) {
            mDialog.dismiss();
        }
    }

onComplete()方法是聯網正常返回,聯網過程結束時執行,在該方法中判斷這個載入框的顯示與否。

   @Override
    public final void onError(Throwable e) {
        if (mDialog.isShowing()) {
            mDialog.dismiss();  
        }
        if (e instanceof EOFException || e instanceof ConnectException || e instanceof SocketException || e instanceof BindException || e instanceof SocketTimeoutException || e instanceof UnknownHostException) {
            Toast.makeText(mContext, "網路異常,請稍後重試!", Toast.LENGTH_SHORT).show();
        } else if (e instanceof ApiException) {
            onError(mWhichRequest, e);
        } else {
            Toast.makeText(mContext, "未知錯誤!", Toast.LENGTH_SHORT).show();
        }
    }

onError(Throwable e)方法稍微複雜一些,整個過程出現異常是會執行這個方法,這裡不只是聯網的過程,還包括對返回資料處理上的異常,比如json解析失敗等。如果發生異常就不會再走onComplete(),所以同樣需要判斷載入框的顯示。下面的是對一些異常的處理,這裡只處理了一些網路方面的異常,可以根據需求新增異常判斷,這裡處理的異常只是出現意外的異常。這裡還自定義了一個異常ApiException,丟擲這個異常都是業務上的問題,比如空資料,格式不對等等,這個是根據返回的狀態值判斷的,方便統一處理錯誤資訊。

public class RxFunction<T> implements Function<HttpResult<T>, T> {
    @Override
    public T apply(@NonNull HttpResult<T> httpResult) throws Exception {
        int retCode = httpResult.getRetCode();
        if (retCode != 200) {
            switch (retCode) {
                case 21001:
                    throw new ApiException("查詢的日期格式錯誤,格式:yyyy-MM-dd");
              //  case 2111:
              //      throw ........
            }
        }
        return httpResult.getResult();
    }
}

這就是這個ApiException產生的地方,也是這句話map(new RxFunction<T>())用到的邏輯,這個是在Retrofit2解析json後得到HttpResult<T>,轉化成Tmap操作符,在轉化過程中根據HttpResult的狀態值,判斷是返回T還是丟擲ApiException異常,這裡可以根據返回狀態值新增不同的錯誤提示資訊,異常會在RxObserveronError(Throwable e)被捕捉,統一傳到View層去處理。

compose(RxSchedulers.<Calendar>io_main())這句話是切換了一下執行緒,等同於這兩句

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

執行在io執行緒,表現在安卓主執行緒。RxSchedulers完整的程式碼:

public class RxSchedulers {

    public static <T> ObservableTransformer<T, T> io_main() {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply( Observable<T> upstream) {
                return upstream.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
}

還有一個類RxManager,這個類是用來管理訂閱的,在RxObserver中,執行onSubscribe方法時,把引數Disposable新增進了RxManager中,看一下RxManager對應的add方法:

    public void add(String key, Disposable disposable) {
        Set<String> keySet = map.keySet();
        if (keySet.contains(key)) {
            CompositeDisposable compositeDisposable = map.get(key);
            compositeDisposable.add(disposable);
        } else {
            CompositeDisposable compositeDisposable = new CompositeDisposable();
            compositeDisposable.add(disposable);
            map.put(key,compositeDisposable );
        }
    }

這裡面,給每一個key初始化了一個CompositeDisposable並存入map,把這個Disposable 加入到對應的CompositeDisposable 中。

RxManager中還有一個方法clear(String key)

    public void clear(String key) {
        Set<String> keySet = map.keySet();
        if (keySet.contains(key)) {
            CompositeDisposable compositeDisposable = map.get(key);
            compositeDisposable.clear();
            map.remove(key);
        }
    }

根據key得到對應的CompositeDisposable ,並執行compositeDisposable.clear()來取消compositeDisposable中所有的訂閱關係。這個RxManagerclear方法建議放在BaseActivityonDestroy()方法中,這也是為什麼前面說的建議這個key採用包名+類名的方式的原因,當這個類銷燬的時候,該類中所有的聯網訂閱關係都會被取消,避免記憶體洩漏。為保證這個map唯一,RxManager採用了單利模式:

    private static RxManager rxManager;
    private Map<String, CompositeDisposable> map;

    private RxManager() {
        map = new HashMap<>();
    }

    public static RxManager getInstance() {
        if (rxManager == null) {
            rxManager = new RxManager();
        }
        return rxManager;
    }

BaseActivity的onDestroy:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RxManager.getInstance().clear(TAG);
    }

以上就是封裝的netWork module的基本使用。
最能體現Retrofit2+Rxjava2優勢的自然是MVP的結構,下面就演示一下MVP的寫法。

MVP

這裡同樣不討論mvp的基礎知識,也不討論幾種mvp哪種寫法更正宗,我覺得只要是程式碼邏輯清楚,耦合性低,都是好的程式碼架構。
這裡為求簡便,PresenterModel沒有寫成介面,直接寫的各自的實現類。先看Model層:

public class MVPModel {

    public Observable<Calendar> getCalendar(String date) {
        return Api.getDefaultService().calendarBean(date).map(new RxFunction<Calendar>()).compose(RxSchedulers.<Calendar>io_main());
    }
}

Model切換執行緒,對請求的資料進行解析,並轉化成我們需要的Observable<Calendar>物件。再看View層:

public interface MVPView {

    void setResult(Calendar calendar);

    void onError(int whichRequest ,Throwable t);

    void onStartLoading(int whichRequest);

    void onEndLoading(int whichRequest);

}

View需要寫成介面,讓對應的Activity或者Fragment去實現,View中除了setResultonError兩個必要的方法外,又寫了onStartLoadingonEndLoading兩個方法,這兩個方法是分別在聯網操作開始和結束時候的回撥,已滿足其他需要在操作開始和結束做的操作,比如不用RxObserver中的載入框,需要其他載入框的,如如SwipeRefreshLayout。再看Presenter

public class MVPPresenter {

    private MVPModel mvpModel;
    private MVPView mvpView;

    public MVPPresenter(MVPView mvpView) {
        this.mvpView = mvpView;
        mvpModel = new MVPModel();
    }

    public void getCalendar(Context context,String date, String key,int whichRequest ,boolean isShowDialog) {

        mvpModel.getCalendar(date).subscribe(new RxObserver<Calendar>(context,key,whichRequest,isShowDialog) {
            @Override
            public void onStart(int whichRequest) {
                super.onStart(whichRequest);
                mvpView.onStartLoading(whichRequest);
            }

            @Override
            public void onSuccess(int whichRequest, Calendar calendar) {
                mvpView.onEndLoading(whichRequest);
                mvpView.setResult(calendar);
            }

            @Override
            public void onError(int whichRequest, Throwable e) {
                mvpView.onError(whichRequest, e);
                mvpView.onEndLoading(whichRequest);
            }
        });
    }
}

Presenter同時持有ModelView的物件,把從Model得到的資料傳到View去處理。最後看View的實現類MVPActivity

public class MVPActivity extends BaseActivity implements MVPView{

    TextView tv_;
    private MVPPresenter mvpPresenter;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_mvp;
    }

    @Override
    protected void initData() {
        tv_ = (TextView) findViewById(R.id.tv_);
        mvpPresenter = new MVPPresenter(this);
    }

    public void request(View view) {
        mvpPresenter.getCalendar(this, "2018-10-01", TAG, 0, false);
    }


    @Override
    public void setResult(Calendar calendar) {
        tv_.setText(calendar.getWeekday());
    }

    /**
     * 如果有多個請求可根據whichRequest處理不同請求的異常
     * @param whichRequest
     * @param t
     */
    @Override
    public void onError(int whichRequest, Throwable t) {

    }

    /**
     * 如果不使用RxObserver自帶的Dialog,可以在RxObserver中設定false,
     * 在onStartLoading和onEndLoading設定需要其他dialog的顯示和消失,如SwipeRefreshLayout
     * @param whichRequest
     */
    @Override
    public void onStartLoading(int whichRequest) {

    }

    @Override
    public void onEndLoading(int whichRequest) {

    }
}

整個Activity看起來非常清爽,由mvpPresenter發出getCalendar()的請求,在setResult中獲得結果,onError中處理異常,onStartLoadingonEndLoading處理載入框,如果一個頁面需要多個請求,可以給RxObserver傳入不同的whichRequest值,根據whichRequest判斷在對應的方法中做不同的操作。

以上就是封裝的這個Retrofit2+Rxjava2的全部內容了,例子中對Retrofit的配置不是很多,如果需要快取等其他配置,可自行新增。