带你封装MVP架构(下)|青训营笔记


带你封装MVP架构(下)|青训营笔记

这是我参与「第四届青训营 」笔记创作活动的的第11天

文章是《带你封装MVP架构》的下篇

前置知识

MVPMVP架构模式详解 - 简书 (jianshu.com)

RetrofitRetrofit使用详解-注解介绍 - 掘金 (juejin.cn)

RxJavaCarson带你学Android:这是一篇清晰易懂的Rxjava入门教程 - 简书 (jianshu.com)

ViewBinding:kotlin-android-extensions插件也被废弃了?扶我起来_guolin的博客-CSDN博客_kotlin-android-extensions废弃

提取Base基类

Base 类中,我们需要做的就是把每个 Activity 或者 Fragment 等这些组件,或者对应的 MVP 层会用到的基本操作以及联系都编写好。后续模块编写的时候直接继承这些 Base 类即可,如此可以免去过多不必要的操作,让开发人员可以把精力更多的花在有意义的地方。

BaseView

BaseView 此接口是MVP模式中,用于各模块链接的关键。我们可以在此处定义好所有 Activity 和 Fragment 可能都会使用到的加载动画的调用方法

public interface BaseView {

    /**
     * 显示加载中
     */
    void showLoading();

    /**
     * 操作成功隐藏dialog和显示成功
     */
    void SuccessHideLoading();

    /**
     * 操作失败隐藏dialog和显示失败
     */
    void FailedHideLoading();

    /**
     * 错误
     *
     * @param bean 错误信息
     */
    @SuppressWarnings("rawtypes")
    void onErrorCode(BaseBean bean);
}

BaseBean/BaseEvent

BaseBean/BaseEvent 这两个的用处一个是抽象出收到的 json 报文的基本字段做 Base类,供错误处理机制来自行判断处理;而 BaseEvent 则是抽象出 EventBus 的消息事件,对每个发送的消息都赋予唯一的 eventCode ,用来做消息事件识别。

public class BaseBean<T> implements Serializable {



    //From 测试图片
    public int code;
    public String msg;

    public String message;

    public T data;


    public BaseBean(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

}
public class BaseEvent<T> {
    private int eventCode;

    private T data;


    public BaseEvent(int eventCode, T data) {
        this.eventCode = eventCode;
        this.data = data;
    }

    public int getEventCode() {
        return eventCode;
    }

    public void setEventCode(int eventCode) {
        this.eventCode = eventCode;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

BaseException

此处自定义了一个异常的基类,供外部构建类或者是获取其中的成员变量。同时它继承自 IOException ,可以作为异常被统一处理,所以也可以用来特定的处理某种异常

public class BaseException extends IOException {


    private String errorMsg;
    private int errorCode;

    public String getErrorMsg() {
        return errorMsg;
    }

    public int getErrorCode() {
        return errorCode;
    }


    public BaseException(String message) {
        this.errorMsg = message;
    }

    public BaseException(String errorMsg, Throwable cause) {
        super(errorMsg, cause);
        this.errorMsg = errorMsg;
    }

    public BaseException(int errorCode, String message) {
        this.errorMsg = message;
        this.errorCode = errorCode;
    }

}

BaseObserver

该类继承自 RxjavaDisposableObserver ,我们创建该类,可以预重写 onStart()onNext(T o)onError(Throwable e) 等方法,预处理错误流;让我们再次继承的时候,只需要简洁的重写 onSuccess() 以及 onError() 即可。这个类的封装,让我们的网络请求变得十分优雅。

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

    protected BaseView view;
    private boolean isShowDialog;

    protected BaseObserver(BaseView view) {
        this.view = view;
    }

    /**
     * 带进度条的初始化方法
     *
     * @param view         view
     * @param isShowDialog 是否显示进度条
     */
    protected BaseObserver(BaseView view, boolean isShowDialog) {
        this.view = view;
        this.isShowDialog = isShowDialog;
    }

    @Override
    protected void onStart() {
        if (view != null && isShowDialog) {
            view.showLoading();
        }
    }

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

    @Override
    public void onError(Throwable e) {
        if (view != null && isShowDialog) {
            view.FailedHideLoading();
        }
        BaseException be;

        if (e != null) {
            //自定义异常
            if (e instanceof BaseException) {
                be = (BaseException) e;
                //回调到view层 处理 或者根据项目情况处理
                if (view != null) {
                    view.onErrorCode(new BaseBean<>(be.getErrorCode(), be.getErrorMsg()));
                } else {
                    onError(be.getErrorMsg());
                }
                //系统异常
            } else {
                if (e instanceof HttpException) {
                    //HTTP错误
                    be = new BaseException(MyUtil.getString(R.string.BAD_NETWORK_MSG), e);
                } else if (e instanceof ConnectException || e instanceof UnknownHostException) {
                    //连接错误
                    be = new BaseException(MyUtil.getString(R.string.CONNECT_ERROR_MSG), e);
                } else if (e instanceof InterruptedIOException) {
                    //连接超时
                    be = new BaseException(MyUtil.getString(R.string.CONNECT_TIMEOUT_MSG), e);
                } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
                    //解析错误
                    be = new BaseException(MyUtil.getString(R.string.PARSE_ERROR_MSG), e);
                } else {
                    be = new BaseException(MyUtil.getString(R.string.OTHER_MSG), e);
                }
            }
        } else {
            be = new BaseException(MyUtil.getString(R.string.OTHER_MSG));
        }
        onError(be.getErrorMsg());
    }

    @Override
    public void onComplete() {
        if (view != null && isShowDialog) {
            view.SuccessHideLoading();
        }
    }


    public abstract void onSuccess(T o);

    public abstract void onError(String msg);

}

BasePresenter

由于我们的 Presenter 层主要作用是连接 View 层和 Model 层,对他们解耦。而当我们的 Model 层的数据处理比较少的时候,例如只有网络请求这种情况,我们就可以直接调用 addDisposable 方法发起请求,而 BaseObserver 也就是被封装好的 Model 层。这种做法是符合 迪米特原则 的方法。

public class BasePresenter<V extends BaseView> {

    private CompositeDisposable compositeDisposable;
    public V baseView;

    /**
     * 这个后面可以直接用   Example:apiServer.login(username, password);
     */
    protected API.SZApi apiServer = RetrofitService.getInstance().getApiService();

    public BasePresenter(V baseView) {
        this.baseView = baseView;
    }

    /**
     * 解除绑定
     */
    public void detachView() {
        baseView = null;
        removeDisposable();
    }

    /**
     * 返回 view
     */
    public V getBaseView() {
        return baseView;
    }

    @SuppressWarnings("all")
    public void addDisposable(Observable<?> observable, BaseObserver observer) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable
                .add(observable.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribeWith(observer));
    }

    private void removeDisposable() {
        if (compositeDisposable != null) {
            compositeDisposable.dispose();
        }
    }

}

BaseActivity/BaseFragment

封装的时候,我们需要设置泛型,传入对应的 Presenter 以及对应的 ViewBinding;最后再继承 BaseView 这个接口

在页面创建和销毁的时候,我们需要对应的做相关的绑定和解绑,这其中除了对 Presenter 层进行绑定,还对 EventBus 做了对应的绑定。这是由于 EventBus 的订阅多会用于 UI 的更新,我们在这两个 Base 类做订阅的注册判断是有必要的;后续的子类想要使用,只需要在写上 @BindEventBus 的注解即可。

关于 EventBus 的封装,可以查看 EventBus封装到项目架构|青训营笔记 - 掘金 (juejin.cn)

public abstract class BaseActivity<P extends BasePresenter<? extends BaseView>,VB extends ViewBinding> extends AppCompatActivity implements BaseView{

    private VB binding;
    /**
     * presenter层的引用
     */
    protected P presenter;

    /**
     * 错误
     *
     * @param bean 错误信息
     */
    @Override
    public void onErrorCode(BaseBean bean) {
        ToastUtil.showToast(bean.msg);
    }

    /**
     * 初始化presenter,也是与Activity的绑定
     *
     * @return 返回new的Presenter层的值
     */
    protected abstract P createPresenter();


    /**
     * 载入view的一些操作
     */
    protected abstract void initView();


    /**
     * 载入数据操作
     */
    protected abstract void initData();


    /**
     * {@inheritDoc}
     * <p>
     * Perform initialization of all fragments.
     *
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(this.getClass().isAnnotationPresent(BindEventBus.class)){
            EventBus.getDefault().register(this);
        }
        UltimateBarX.statusBarOnly(this)
                .light(true)
                .transparent()
                .apply();
        //强制使用竖屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        binding = ViewBindingUtil.inflateWithGeneric(this, getLayoutInflater());
        setContentView(binding.getRoot());
        presenter = createPresenter();
        initView();
        initData();
    }

    /**
     * 解除presenter与Activity的绑定
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(this.getClass().isAnnotationPresent(BindEventBus.class)){
            EventBus.getDefault().unregister(this);
        }
        if (presenter != null){
            presenter.detachView();
        }
    }

    @Override
    public void showLoading() {
        MyUtil.showLoading(this);
    }

    @Override
    public void SuccessHideLoading() {
        MyUtil.dismissSuccessLoading();
    }

    @Override
    public void FailedHideLoading() {
        MyUtil.dismissFailedLoading();
    }

    /**
     * 查看当前是否为深色模式
     *
     * @param context 传入当前context
     * @return 返回ture 偶然false
     */
    public Boolean getDarkModeStatus(Context context){
        int mode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        return mode == Configuration.UI_MODE_NIGHT_YES;
    }

    protected VB getBinding() {
        return binding;
    }
}
public abstract class BaseFragment<P extends BasePresenter,VB extends ViewBinding> extends Fragment implements BaseView {

    protected Context mContext;

    protected P presenter;

    private VB binding;


    /**
     * 显示加载中
     */
    @Override
    public void showLoading() {
        MyUtil.showLoading(mContext);
    }

    /**
     * 操作成功隐藏dialog和显示成功
     */
    @Override
    public void SuccessHideLoading() {
        MyUtil.dismissSuccessLoading();
    }

    /**
     * 操作失败隐藏dialog和显示失败
     */
    @Override
    public void FailedHideLoading() {
        MyUtil.dismissFailedLoading();
    }

    /**
     * 创建 presenter
     *
     * @return presenter
     */
    protected abstract P createPresenter();


    /**
     * 在这里要返回view的根路径
     *
     * @return 返回绑定的view
     */
    protected VB getBinding() {
        return binding;
    }

    /**
     * 初始化布局
     */
    protected abstract void initView();

    /**
     * 初始化数据
     */
    protected abstract void initData();


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        if(this.getClass().isAnnotationPresent(BindEventBus.class)){
            EventBus.getDefault().register(this);
        }
        binding = ViewBindingUtil.inflateWithGeneric(this, inflater, container, false);
        //得到context,在后面的子类Fragment中都可以直接调用
        mContext = ActivityUtil.getCurrentActivity();
        presenter = createPresenter();
        initView();
        initData();
        return binding.getRoot();
    }

    @Override
    public void onResume() {
        super.onResume();
        initListener();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if(this.getClass().isAnnotationPresent(BindEventBus.class)){
            EventBus.getDefault().unregister(this);
        }
        //销毁时,解除绑定
        if (presenter != null) {
            presenter.detachView();
        }
        binding = null;
    }

    private void initListener() {
    }

    @Override
    public void onErrorCode(BaseBean bean) {
    }


}

至此,MVP 的封装就完成了,更多细节和使用请查看我的开源库:dyjcow/qxy_potato (github.com)

参考

带你封装自己的MVP+Retrofit+RxJava2框架(二) - 掘金 (juejin.cn)

yechaoa/wanandroid_java: 🎨 玩安卓客户端 ,MD + Retrofit + RxJava + MVP + AndroidX (github.com)


文章作者: DYJ
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 DYJ !
评论
  目录