带你写一个不一样的 MVP 开发框架

1,552 阅读6分钟
原文链接: www.jianshu.com

与孤独为伴,让自己来一场涅槃

项目GitHub地址:https://github.com/ms-liu/ProjectFrameDemo

一、了解

  在传统的Android项目开发过程中,按照MVC模块划分的话,往往会发现其实并不能很好的划分出各自的职责出来,Activity有时既需要扮演C的角色还需要扮演V的角色。这就导致Activity中代码耦合现象严重,尤其对于某些业务逻辑相对复杂的页面,动不动就是上千行的代码。
  可能对于这些页面一代开发人员来说还能很愉快的接受,但是当新人来维护时,这就会让他很头痛了;
  “他在写些什么? (黑人问号 黑人问号)他应该是个大神,代码我都看不懂,我要好好研究研究!”
  在这种现象的基础上,便有了MVP的开发模式:

  • Model:数据层——业务逻辑和实体类
  • View:视图层——页面展示
  • Presenter:逻辑层——数据和视图层交互

  单单从目前的分析来看,MVP和MVC并没有什么大的区别,当然这两者本身本质上的区别不大,都是用作解耦V和M,只是将MVC套到Android开发过程中的时候,让Activity等一些组件,角色扮演不是那么清晰,所以导致了问题产生。
  而在MVP的模式中,将Activity这些组件完全当成View层,让Activity职责单一化。Presenter负责数据处理,然后通过接口的形式达到与View交互的目的。让View和Model不在有交集。


MVP结构


  这篇文章不作MVP如何编写的讲解,如需了解可自行百度,另外项目里面也包含了MVP 编写代码。

二、提升与改进(重点与目的

  其实有过了解或者使用MVP的开发人员,应当都能体会感受到,MVP确实能够将Activity中代码简化。但是对于那些业务复杂界面,Presenter中代码也是会急剧增多,并且有时也会将View层中代码放到Presenter中,重新回到老状态。
  另外不知道有没有人碰到和我一样的问题,就是在使用MVP的过程中,有时在打开多个Activity页面后,回退过程中,在某些使用Fragment的Activity中会报出NullPointException,这是因为在我们打开过多页面时,由于内存和生命周期管理导致Fragment被系统回收,但是我们并没有将这些告知Presenter,从而发生NullPointException
  下面我们就一起来解决,这些问题。

(一)框架分析

MVPP结构


  在该框架中,我们在V和P之间加上了一个Proxy或Controller代理类,我们将会尽量少的让P和V直接进行交互接触,而是通过Proxy与V进行交互,在Proxy中预先处理部分逻辑,从而达到减轻Presenter职责的目的,让Presenter中代码更加简洁,更加专注于业务处理逻辑。

(二)Code

代码分包展示

1、Model层

  • 定义IModel数据接口
    public interface IModel<T> {
      void setModel(T t);
      T getModel();
    }
  • 实现IModel接口

    public class ImproveModelImpl implements IModel<ImproveInfoBean>{
      private ImproveInfoBean mModel;
      @Override
      public void setModel(ImproveInfoBean improveInfoBean) {
          this.mModel = improveInfoBean;
      }
    
      @Override
      public ImproveInfoBean getModel() {
          if (this.mModel == null){
              this.mModel = new ImproveInfoBean();
          }
          return mModel;
      }
    }

2、View层代码

  • 定义View生命周期监听接口

    public interface OnViewStateListener {
    
      void onCreate();
    
      void onPause();
    
      void onResume();
    
      void onStop();
    
      void onDestroy();
    }
  • 定义公共IView接口

    public interface IView {
      // 绑定对View生命周期监听
      void bindListener(OnViewStateListener listener);
    
      Context getContext();
    
       // Toast提示
      void showToast(String message);
    
       //显示加载对话框
      void showLoadingDialog(String message);
    
      // 隐藏加载对话框
      void hideLoadingDialog();
    }
  • 编写ViewDelegate
    对 View操作的委托类,实现IView和OnViewStateListener接口中的方法

    public class ViewDelegate implements IView,OnViewStateListener {
    
      private Context mCtx;
      private ProgressDialog mProgressDialog;
    
      public ViewDelegate(Context context){
          this.mCtx = context;
      }
    
      private List<OnViewStateListener> mOnViewStateListeners ;
    
      @Override
      public void bindListener(OnViewStateListener listener) {
          //用数组管理每一个View的生命周期,避免一个页面有多个监听
          if (mOnViewStateListeners == null){
              mOnViewStateListeners = new ArrayList<>();
              mOnViewStateListeners.add(listener);
          }else {
              if (!mOnViewStateListeners.contains(listener)){
                  mOnViewStateListeners.add(listener);
              }
          }
      }
    
      @Override
      public Context getContext() {
          return mCtx;
      }
    
      @Override
      public void showToast(String message) {
          if (mCtx != null) {
              Toast.makeText(mCtx, message, Toast.LENGTH_SHORT).show();
          }
      }
    
      @Override
      public void showLoadingDialog(String message) {
          if (mCtx != null){
              mProgressDialog = new ProgressDialog(mCtx);
          }
      }
    
      @Override
      public void hideLoadingDialog() {
          if (mProgressDialog != null && mProgressDialog.isShowing()){
              mProgressDialog.hide();
          }
      }
    
      //------------------View生命周期管理--------------------------------------------------------
    
      @Override
      public void onCreate() {
          if (checkListener()){
              for (OnViewStateListener listener:
                   mOnViewStateListeners) {
                  listener.onCreate();
              }
          }
      }
    
      @Override
      public void onPause() {
          if (checkListener()){
              for (OnViewStateListener listener:
                      mOnViewStateListeners) {
                  listener.onPause();
              }
          }
      }
    
      @Override
      public void onResume() {
          if (checkListener()){
              for (OnViewStateListener listener:
                      mOnViewStateListeners) {
                  listener.onResume();
              }
          }
      }
    
      @Override
      public void onStop() {
          if (checkListener()){
              for (OnViewStateListener listener:
                      mOnViewStateListeners) {
                  listener.onStop();
              }
          }
      }
    
      @Override
      public void onDestroy() {
          if (checkListener()){
              for (OnViewStateListener listener:
                      mOnViewStateListeners) {
                  listener.onDestroy();
              }
          }
      }
    
      private boolean checkListener(){
          return mOnViewStateListeners != null && !mOnViewStateListeners.isEmpty();
      }
    }
  • 编写BaseActivity,在里面实现页面公共操作方法

    public class BaseActivity extends AppCompatActivity implements IView{
    
      private ViewDelegate mDelegate;
    
      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
    
          //可以考虑注解创建
          mDelegate = new ViewDelegate(this);
    
      }
    
      @Override
      public void bindListener(OnViewStateListener listener) {
          mDelegate.bindListener(listener);
      }
    
      @Override
      public Context getContext() {
          return BaseActivity.this;
      }
    
      @Override
      public void showToast(String message) {
          mDelegate.showToast(message);
      }
    
      @Override
      public void showLoadingDialog(String message) {
          mDelegate.showLoadingDialog(message);
      }
    
      @Override
      public void hideLoadingDialog() {
          mDelegate.hideLoadingDialog();
      }
    }

3、Presenter层代码

  • IPresenter公共接口,因为要监听View生命周期,让它继承OnViewStateListener 接口

    public interface IImprovePresenter<M,V extends IView> extends OnViewStateListener {
    
     /**
      * View绑定
      * @param v
      */
     void bindView(V v);
    
     /**
      * 数据加载
      * @return
      */
     M loadModel();
    
     /**
      * View解绑
      */
     void detachView();
    }
  • 编写BasePresenter,实现部分共方法

    public abstract class BaseImprovePresenter<M extends IModel,V extends IView> implements IImprovePresenter<M,V> {
    
      private List<String> mMethods;
    
      public BaseImprovePresenter(){
          this.mMethods = new ArrayList<>();
      }
    
      @Override
      public void onCreate() {
    
      }
    
      @Override
      public void onPause() {
    
      }
    
      @Override
      public void onResume() {
    
      }
    
      @Override
      public void onStop() {
    
      }
    
      @Override
      public void onDestroy() {
    
      }
      //---------------------模拟需要在生命周期中处理的逻辑--------------------------------------
      /**
       * 添加View生命周期结束时,需要结束的方法名称;
       * 类似RxJava中添加addSubscription()
       * @param methodName
       */
      public void addHandleMethod(String methodName){
          if (mMethods != null){
              mMethods.add(methodName);
          }
      }
    
      /**
       * 清楚
       * 类似RxJava中添加clearSubscription()
       */
      public void clearMethod(){
          if (mMethods != null && !mMethods.isEmpty()){
              mMethods.clear();
          }
        }
    }
  • 编写Presenter,

    public class ImprovePresenter extends BaseImprovePresenter<ImproveModelImpl,IImproveView> {
    
      private IImproveView mView;
      private ImproveModelImpl mModel;
    
      @Override
      public void bindView(IImproveView iImproveView) {
          this.mView = iImproveView;
          mModel = loadModel();
          mView.showExplain(mModel.getModel().explain);
    
      }
    
      @Override
      public ImproveModelImpl loadModel() {
          addHandleMethod("异步请求数据方法");
          return new ImproveModelImpl();
      }
    
      @Override
      public void detachView() {
          clearMethod();
      }
    
      public void setInfo(String username, String password) {
          if (mModel != null){
              mModel.getModel().setName(username);
              mModel.getModel().setPassword(password);
          }
      }
    
      public void getInfoBean(){
          mView.showInfo("用户名:"+mModel.getModel().getName()+"\r\n密码:"+mModel.getModel().getPassword());
      }
    }
  • 编写PresenterProxy代理类

    public class ImprovePresenterProxy implements IImprovePresenter<ImproveModelImpl,IImproveView>, View.OnClickListener {
    
      private ImprovePresenter mPresenter;
      private EditText etUserName;
      private EditText etPassword;
      private TextView componentShowInfo;
      private TextView componentExplain;
      private IImproveView mView;
    
      public ImprovePresenterProxy(ImprovePresenter improvePresenter){
          //判空
          checkPresenter(improvePresenter);
          //可以考虑依赖注入 方式
          this.mPresenter = improvePresenter;
          loadModel();
      }
    
      private void checkPresenter(ImprovePresenter improvePresenter) {
          if (improvePresenter == null){
              throw new NotBindPresenterException();
          }
      }
    
      @Override
      public ImproveModelImpl loadModel() {
          return mPresenter.loadModel();
      }
    
      @Override
      public void bindView(IImproveView iImproveView) {
          checkView(iImproveView);
          this.mView = iImproveView;
          mPresenter.bindView(iImproveView);
      }
    
      private void checkView(IImproveView iImproveView) {
          if (iImproveView == null){
              throw new NotBindViewException();
          }
      }
    
      @Override
      public void detachView() {
          mPresenter.detachView();
      }
    
      @Override
      public void onCreate() {
          //to do something
      }
    
      @Override
      public void onPause() {
          //to do something
      }
    
      @Override
      public void onResume() {
          //to do something
      }
    
      @Override
      public void onStop() {
          //to do something
      }
    
      @Override
      public void onDestroy() {
          detachView();
      }
    
      @Override
      public void onClick(View view) {
          int i = view.getId();
          if (i == R.id.btn_save) {
              if (etUserName != null && etPassword != null){
                  mView.showToast("保存成功");
                  mPresenter.setInfo(etUserName.getText().toString(),etPassword.getText().toString());
              }
          } else if (i == R.id.btn_get) {
              mPresenter.getInfoBean();
          } else {
    
          }
      }
    
      public void setComponentName(EditText etUsername) {
          this.etUserName = etUsername;
      }
    
      public void setComponentPassword(EditText etPassword) {
          this.etPassword = etPassword;
      }
    
      public void setComponentShowInfo(TextView componentShowInfo) {
          this.componentShowInfo = componentShowInfo;
      }
    
      public void setComponentExplain(TextView componentExplain) {
          this.componentExplain = componentExplain;
      }
    }

  至此,完成对MVP框架的完善和升级,完善后的MVP能够更好的用于实际生产,做到进一步的代码解耦的目的。并且由于加入了对View生命周期的管理,也很好的解决NullPointException问题。

实际代码请下载或者Frok项目。

项目GitHub地址:https://github.com/ms-liu/ProjectFrameDemo

欢迎大家给出中肯的建议和提高意见,大家一起学习进步。

个人邮箱:ms_liu163@163.com

QQ:275846421