基于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而已,对于我们而言是极易掌握的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值