带你封装MVP架构(下)|青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第11天
文章是《带你封装MVP架构》的下篇
前置知识
MVP:MVP架构模式详解 - 简书 (jianshu.com)
Retrofit:Retrofit使用详解-注解介绍 - 掘金 (juejin.cn)
RxJava:Carson带你学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
该类继承自 Rxjava
的 DisposableObserver
,我们创建该类,可以预重写 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)