基於RxJava+Retrofit實現的MVP基礎框架

基於RxJava+Retrofit實現的MVP基礎框架

  • 本文基於前文 MVP初步實踐而改進的,請先查看前文。
  • 本文需要有RxJava2和Retrofit的基礎,否則請查看前文 MVP初步實踐
問題引出

  看過上文的MVP初步實踐,則應該知道了MVP的實現的基本思路。就是通過對Model,View,Presenter進行一些封裝,從而連接它們之間的關係。但是,上文僅僅是對於普通的項目進行的封裝,而在實際中,我們通常會導入RxJava和Retrofit來方便開發。
  因此,這裏將會基於RxJava+Retrofit來豐富我們的MVP框架。當然,要繼續向下看的必須具有一些RxJava的基礎和Retrofit的基礎。而若只是想要了解MVP模式的話,只看前面一篇文章就夠了,此篇是爲了能夠在實際中使用而進行的封裝。

包結構

包結構
  可以看到整體上包結構是沒喲什麼變化的,只是多了一個NetWork包。其中兩個類分別是基本觀察者和管理連接的管理器,都會在後面進行詳解。

依賴導入

  要使用Retrofit和RxJava的前提必須先要導入這二者的依賴,直接在對應的common的build.gradle中導入即可。

dependencies {
   ...
   
    // RxJava
    api 'io.reactivex.rxjava2:rxandroid:2.1.1'
    api 'io.reactivex.rxjava2:rxjava:2.2.8'
    // Retrofit
    api 'com.squareup.retrofit2:retrofit:2.5.0'
    // 轉換
    api 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
    api 'com.squareup.retrofit2:converter-gson:2.5.0'
}

  當然這裏的版本都是可以修改的,可以從github上查看最新的版本號。另外這裏沒有使用implementation而是使用了api,原因是爲了在其他的module中直接使用這兩個庫,而不必重新在其他module中再添加一次依賴。

MVP接口

  對於上文中的MVP三個接口中,View和Presenter都是不必修改的,因爲本身它們就不需要改變。但是對於Model接口而言,卻是多了兩個方法。分別是對連接的管理和對線程的切換。

public interface Model {
    /**
     * 數據請求切換線程
     *
     * @return ObservableTransformer
     */
    <T> ObservableTransformer<T, T> switchThread();

    /**
     * 移除所有網絡請求
     */
    void removeDisposable();
}

  switchThread方法是對於線程的切換,由於我們使用的是RxJava和Retrofit進行的網絡請求,所以這裏我們沒有再使用子線程和Handler來進行線程的切換,而是直接通過RxJava提供給我們的方法來進行切換線程。
  另外一個方法是用來移除連接請求的,我們一個Model可能對應多個獲取數據的請求,而當View與Presenter斷開連接的時候,我們應該也同時斷開請求數據的連接。因爲當view斷開連接的時候,我們即使獲取到了數據也是沒用的了。

Presenter
public abstract class BasePresenter<V extends View, M extends Model> implements Presenter {
    protected V mView;
    protected M mModel;

    public BasePresenter(V view) {
        mView = view;
        mModel = createModel();
    }

    @Override
    public void detach() {
        mView = null;
        mModel.removeDisposable();
    }

    @Override
    public boolean isAttach() {
        return mView != null;
    }

    @Override
    public void checkAttach() {
        if (!isAttach()){
            throw new RuntimeException("當前Presenter未與View建立連接");
        }
    }

    /**
     * 創建Presenter對應的Model
     *
     * @return Model
     */
    protected abstract M createModel();
}

  對於Presenter,我們僅僅是在detach方法中調用了Model的removeDisposable方法來移除連接。其實這個方法在前面的基礎的MVP中也應該存在的,至於爲什麼沒有添加,是因爲在前面並不知道到底使用的是什麼網絡請求,所以也就沒辦法控制。

View

  對於兩個BaseActivity/BaseFragment而言,它們均是不變的。因爲我們引入的RxJava+Retrofit實際上只是爲了方便數據獲取的,也就是說理論上應該只涉及到Model方面。而實際上也的確如此,至於上面的Presenter的改變,完全是因爲前面搭建的基礎框架少了一個移除連接的方法。

Model
public class BaseModel implements Model {

    protected DisposableManager mDisposableManager;

    public BaseModel() {
        this.mDisposableManager = new DisposableManager();
    }

    /**
     * 數據請求切換線程,io線程請求數據,請求完後在主線程進行操作
     *
     * @param <T> 泛型,應當是BaseData<?>
     * @return 轉換後的結果
     */
    @Override
    public <T> ObservableTransformer<T, T> switchThread() {
        return upstream ->
                upstream.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
    }

    @Override
    public void removeDisposable() {
        mDisposableManager.clearDisposable();
    }
}

  BaseModel纔是本次修改的主要變動者,可以看到的是,切換線程中我們是使用的RxJava的io線程進行獲取數據的操作,而操作完之後是在UI線程進行處理。至於不太明白的建議再看看RxJava的文檔。
  另外對於連接的管理使用的是DisposableManager ,而DisposableManager 是我們自己定義的類,那麼具體是怎麼實現的呢?

DisposableManager
public class DisposableManager {
    /**
     * 管理所有的請求
     */
    private CompositeDisposable mCompositeDisposable;

    /**
     * 將請求對象添加進來,用於統一管理
     *
     * @param disposable 具體請求
     */
    public void addDisposable(Disposable disposable) {
        if (mCompositeDisposable == null) {
            mCompositeDisposable = new CompositeDisposable();
        }
        if (!mCompositeDisposable.isDisposed() && disposable != null) {
            mCompositeDisposable.add(disposable);
        }
    }

    /**
     * 移除所有的請求並結束
     */
    public void clearDisposable() {
        if (mCompositeDisposable.isDisposed()) {
            return;
        }
        mCompositeDisposable.clear();
    }
}

  從實現上看DisposableManager內部又是通過CompositeDisposable來管理連接的,這個類是RxJava提供給我們的,是專門用於管理Disposable的。
  而Disposable又是何時被添加進來的呢?

BaseObserver
public abstract class BaseObserver<T> extends DisposableObserver<T> {

    protected BaseObserver(DisposableManager disposableManager) {
        disposableManager.addDisposable(this);
    }

    @Override
    public void onNext(T t) {
        onSuccess(t);
    }

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

    @Override
    public void onComplete() {
    }

    /**
     * 數據成功返回的回調
     *
     * @param t 獲取到的數據
     */
    public abstract void onSuccess(T t);

    /**
     * 請求數據出現錯誤的回調
     *
     * @param throwable 異常信息
     */
    public abstract void onFail(Throwable throwable);
}

  可以看到的是,我們使用的是DisposableObserver來作爲觀察者,也就是說這是一個可關閉連接的觀察者。當我們在創建這個對象的時候就已經把它添加進了DisposableManager,也就是說,我們進行RxJava鏈式調用的時候應該傳遞BaseObserver。

總結

  至此,基於RxJava+Retrofit的封裝已經結束了,可是我們看到是並沒有進行其他封裝啊,僅僅是將Model修改了一下而已。而實際上,也的確沒看出來與前面的基礎MVP有什麼區別,因爲只有在實踐中才能看出封裝的結果。

案例(使用方式)

  案例的話依舊是前面那個案例,點擊按鈕,獲取數據。

  這裏將會採取通過一個網絡請求的方式來進行案例的演示。

Service
public interface MainService {
    @GET("/api")
    Observable<BaseData<String>> getTextViewText(@Query("id") String id);
}

  網絡請求採用的是Retrofit的方式,若是不太明白的話建議先去看一下Retrofit的文檔。

RetrofitClient
public class RetrofitClient {

    /**
     * Retrofit實例,用於創建service
     */
    private Retrofit mRetrofit;
    /**
     * Retrofit單實例,用於單例模式
     */
    private static RetrofitClient mRetrofitClient;

    private RetrofitClient() {
        init();
    }

    /**
     * 獲取RetrofitClient的單例
     *
     * @return RetrofitClient實例
     */
    public static RetrofitClient getInstance() {
        if (mRetrofitClient == null) {
            synchronized (RetrofitClient.class) {
                if (mRetrofitClient == null) {
                    mRetrofitClient = new RetrofitClient();
                }
            }
        }
        return mRetrofitClient;
    }


    /**
     * 初始化Retrofit
     */
    private void init() {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                .retryOnConnectionFailure(true);
        Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl("http://www.example.com");

        mRetrofit = retrofitBuilder.client(clientBuilder.build()).build();
    }

    /**
     * 產生對應的Service
     *
     * @param tClass Service的class類型
     * @param <T>    泛型
     * @return Service實例
     */
    public <T> T createService(Class<T> tClass) {
        return mRetrofit.create(tClass);
    }

}

  這裏封裝了一個RetrofitClient,所有的service將會通過createService進行獲取。另外,該類採用的是單例模式,因爲它涉及到的是Retrofit的配置,因此採用單例模式只配置一次即可。若是對於其中的配置不太明白的話建議多看看Retrofit的文檔。

  萬事具備,接下來就是MVP的對應的類了。

View

  根據之前的分析我們可以知道,此次封裝的是關於數據請求的,因此對於View而言,是完全不需要改變的,從這裏我們也能看出MVP解耦的一些好處。

Presenter
public class MainPresenter extends BasePresenter<MainActivity, MainModel> implements MainContract.Presenter {
    MainPresenter(MainActivity view) {
        super(view);
    }

    private String[] params = {"success", "fail"};
    private Random random = new Random(System.currentTimeMillis());

    @Override
    protected MainModel createModel() {
        return new MainModel();
    }

    @Override
    public void updateTextViewText() {
        // 請求開始時顯示進度條
        mView.showProgress();

        // 模擬獲取數據的幾種狀態,這裏通過傳遞的參數來決定是否獲取數據成功
        int index = random.nextInt(2);

        mModel.getTextString(params[index], new ModelCallBack() {
                    @Override
                    public void success(BaseData<?> baseData) {
                        checkAttach();
                        String s = (String) baseData.getData();
                        if (!TextUtils.isEmpty(s)) {
                            mView.updateText((String) baseData.getData());
                        }
                        mView.hideProgress();
                        mView.showToast(baseData.getMessage());
                    }

                    @Override
                    public void fail(Throwable throwable) {
                        checkAttach();
                        // 檢查錯誤信息,該部分可放到BaseObserver中
                        String errorMsg ;
                        if (throwable instanceof JsonParseException) {
                            errorMsg = "Json解析失敗!";
                        } else if (throwable instanceof HttpException) {
                            errorMsg = "Http錯誤!";
                        } else {
                            errorMsg = "其他錯誤!";
                        }

                        mView.showToast(errorMsg);
                        mView.hideProgress();
                    }
                }

        );
    }
}

  對於Presenter而言也是不需要改變的,也就是說需要改變的其實只是Model而已。

Model
public class MainModel extends BaseModel implements MainContract.Model {
    @Override
    public void getTextString(String param, ModelCallBack callBack) {

        RetrofitClient.getInstance()
                .createService(MainService.class)
                .getTextViewText(param)
                .compose(switchThread())
                .subscribe(new BaseObserver<BaseData<String>>(mDisposableManager) {

                    @Override
                    public void onSuccess(BaseData<String> stringBaseData) {
                        callBack.success(stringBaseData);
                    }

                    @Override
                    public void onFail(Throwable throwable) {
                        //callBack.fail(e);

                        /*
                         * 由於請求的鏈接並不存在,最終的結果將會返回到onFail這裏
                         * 因此,將會在這裏模擬成功和失敗的情況
                         * 另外又加入2秒延遲代表請求的過程
                         */
                        new Handler().postDelayed(() -> {
                            if ("success".equals(param)) {
                                BaseData<String> baseData = new BaseData<>();
                                baseData.setMessage("獲取數據成功!");
                                baseData.setData("我是獲取的數據");
                                callBack.success(baseData);
                            } else {
                                callBack.fail(throwable);
                            }
                        }, 2000);
                    }
                });
    }
}

  Model也是無比的簡單,僅僅就一個鏈式調用就完成了網絡請求和線程的切換。線程的切換在.compose(switchThread()),這是RxJava提供的方法,我們僅僅是將在BaseModel中實現的方法調用而已。

  至此,對於Rxjava+Retrofit 的使用已經很是清晰了,相對於基礎版的MVP而言,要改變的僅僅是Model而已,對於我們而言是極易掌握的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章