基于RxJava+Retrofit实现的MVP基础框架
基于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 点击查看