基於RxJava+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而已,對於我們而言是極易掌握的。
- 附:代碼上傳github 點擊查看