MVP开发模式解析



前言

由于项目里同事用到MVP开发模式,我看了几篇关于 MVP 的文章,对其有了基本的了解之后,便照猫画虎进行了开发,之后便再也没接触过 MVP。

最近空闲读了些MVP的文章,受益匪浅,于是打算写一篇关于MVP开发的文章,一方面作为自己学习的笔记便于查看,另一方面希望能帮助没有接触过 MVP 模式的新人提供帮助,以便可以快速入门。

什么是MVC

在讲MVP之前先讲讲MVC

MVC结构图

MVC即Model-View-Controller。M:逻辑模型,V:视图模型,C:控制器。

Model 层:用来定义实体对象,处理业务逻辑,可以简单地理解成 Java 中的实体类。

View 层:负责处理界面的显示,在 Android 中对应的就是 xml 文件。

Controller 层:对应的是 Activity/Fragment ,当加载完成 xml 布局之后,我们需要找到并设置布局中的各个 View,处理用户的交互事件,更新 View 等。

在MVC模式中,Controller扮演者重要的角色,它不仅要处理 UI 的显示与事件的响应,还要负责与 Model 层的通信,同时 Model 层与 View 层也会通信,三者的耦合度很大。

作为安卓中默认的开发模式,MVC易于上手,适合快速开发小型项目,但是随着业务逻辑的复杂度越来越大,View(Activity/Fragment)层代码就会越来越臃肿,因为它同事承担着Controller 与 View 的角色,这对于项目后期的更新维护与测试交接都是非常不方便的,大大提高了生产成本,于是MVP就应运而生。

什么是MVP

MVP结构图

MVP是MVC的升级进化,全称Model(模型层)、View(视图层)、Presenter(主持者)。从结构图中我们可以看到,Presenter直接替代了Controller,去除了了Model与View的直接关联与耦合

Model 层:与MVC中的Model层一样,用来定义实体对象,处理业务逻辑

View 层:视图层,在MVP模式中不仅对应 xml 文件, Activity/Fragment也属于视图层,View 层现在不仅作为 UI 的显示,还负责响应生命周期的变化

Presenter层:主持者层,Model层与View层沟通的桥梁,处理业务逻辑,它响应View的请求从Model层获取数据,再将数据返回给View层,View实现Ui更新。

在 MVP 的架构中,最大的特点就是 View 与 Model 之间的解耦,两者之间必须通过 Presenter 来进行通信,使得视图和数据之间的关系变得完全分离。

MVP的基本实现方式

1.创建Presenter接口,把所有的业务逻辑接口放在这里,并创建它的实现类PresenterImpl

2.创建Iview接口,把所有视图逻辑的接口都放在这里,它的实现类是Activity/Fragment

3.在Activity/Fragment中包含了一个Presenter实例,而PresenterImpl又包含了一个IView的实例,并且依赖Model,Activity/Fragment 只保留对 IPresenter 的调用,当 View 层发生某些请求响应或者生命周期发生变化,则会迅速的向 Presenter 层发起请求,让 Presenter 做出相应的处理。

现在我们用MVP模式具体实现它

Presenter接口

public interface MainPresenter {

    void onResume(MainActivity mainActivity);//页面初始化,Presenter 被激活

    void onDestory();//页面结束,Presenter 结束,为了避免相互持有引用而导致的内存泄露

    void loadMore(MainActivity mainActivity);//加载更多
}

IView 接口:

public interface MainView {

    void onNext(String resulte, String mothead); //拿到model层返回的数据,实现UI的显示

    void onError(ApiException e);//失败异常处理

    void showLoadingDialog();//显示加载网络的弹框

    void dialogDissmiss();//网络请求成功或失败,结束加载网络的弹框
}

Presenter接口的实现类

public class MainPresenterImpl implements MainPresenter, MainInteractor.OnGetDataResultFinishListener {

        private MainView mainView;
        private MainInteractorImpl mainInteractor;

        public MainPresenterImpl(MainView mainView, MainInteractorImpl mainInteractor) {
            this.mainView = mainView;
            this.mainInteractor = mainInteractor;
        }

        @Override
        public void onNext(String resulte, String mothead) {
            if (mainView != null) {
                mainView.onNext(resulte, mothead);
                mainView.dialogDissmiss();
            }
        }

        @Override
        public void onError(ApiException e) {

        }

        @Override
        public void onResume(MainActivity mainActivity) {
            if (mainView != null) mainView.showLoadingDialog();
            SubjectApi postEntity = new SubjectApi();
            postEntity.setStart(0);
            postEntity.setAreaId("");
            postEntity.setMothed("SSLJ_CORE_PLATFORM/circles/datatable" + "/" + 0);
            mainInteractor.getDataResult(this, postEntity, mainActivity);
        }

        @Override
        public void onDestory() {
            mainView = null;
        }

        @Override
        public void loadMore(MainActivity mainActivity) {
            SubjectApi postEntity = new SubjectApi();
            postEntity.setStart(0);
            postEntity.setAreaId("");
            postEntity.setMothed("SSLJ_CORE_PLATFORM/circles/datatable" + "/" + 0);
            mainInteractor.getDataResult(this, postEntity, mainActivity);
        }
}

IView 接口的实现类 MainActivity

 public class MainActivity extends RxAppCompatActivity implements MainView,    RecyclerArrayAdapter.OnLoadMoreListener {

        private LinearLayoutManager linearLayoutManager;
        private SnsBlogAdapter adapter;
        private MainPresenter mainPresenter;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            initView();
            initData();
        }

        private void initView() {
            ActivityMainTestBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main_test);
            linearLayoutManager = new LinearLayoutManager(this);
            binding.recyclerView.setLayoutManager(linearLayoutManager);
            binding.recyclerView.setAdapter(adapter = new SnsBlogAdapter(this));
            adapter.setMore(R.layout.view_more, this);
            adapter.setOnItemClickListener(new RecyclerArrayAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(int position) {

                }
            });
            adapter.setError(R.layout.view_error, new RecyclerArrayAdapter.OnErrorListener() {
                @Override
                public void onErrorShow() {
                    adapter.resumeMore();
                }

                @Override
                public void onErrorClick() {
                    adapter.resumeMore();
                }
            });
        }

        private void initData() {
            mainPresenter = new MainPresenterImpl(this, new MainInteractorImpl());
            mainPresenter.onResume(this);
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            mainPresenter.onDestory();
        }

        @Override
        public void onLoadMore() {
            mainPresenter.loadMore(this);
        }

        @Override
        public void onNext(String resulte, String mothead) {
            Gson gson=new Gson();
            List<SnsCard> items = gson.fromJson(resulte, new TypeToken<List<SnsCard>>() {}.getType());
            adapter.addAll(items);
        }

        @Override
        public void onError(ApiException e) {

        }

        @Override
        public void showLoadingDialog() {
            Toast.makeText(this, "正在加载", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void dialogDissmiss() {
            Toast.makeText(this, "加载完成", Toast.LENGTH_SHORT).show();
        }
}

Model层

public class SnsCard {

  private String userName;

  public SnsCard(String userName) {
    this.userName=userName;
  }

  public String getUserName() {
    return userName;
  }

  public void setUserName(String userName) {
    this.userName = userName;
  }
}

model 具体业务逻辑的实现

public class MainInteractorImpl implements MainInteractor,HttpOnNextListener {
    @Override
    public void getDataResult(final OnGetDataResultFinishListener listener, SubjectApi baseApi, MainActivity rxAppCompatActivity) {
        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                Gson gson = new Gson();
                String str = gson.toJson(createSnsCardList());
                listener.onNext(str,"");
            }
        }, 2000);
//        new HttpManager(this,rxAppCompatActivity).doHttpDeal(baseApi);
    }
    private List<SnsCard> createSnsCardList() {
        List<SnsCard> SnsCards=new ArrayList<>();
        for (int i = 0; i <10 ; i++) {
            SnsCards.add(new SnsCard("jerry"+i));
        }
        return SnsCards;
    }

    @Override
    public void onNext(String resulte, String mothead) {

    }

    @Override
    public void onError(ApiException e) {

    }
}

Model提供给Presenter层调用的接口和提供返回数据给Presenter层的接口

public interface MainInteractor {
    interface OnGetDataResultFinishListener{

        /**
         * 成功后回调方法
         *
         * @param resulte
         * @param mothead
         */
        void onNext(String resulte, String mothead);

        /**
         * 失败
         * 失败或者错误方法
         * 自定义异常处理
         *
         * @param e
         */
        void onError(ApiException e);
    }
   //获取网络数据
    void getDataResult(OnGetDataResultFinishListener listenter, SubjectApi baseApi, MainActivity rxAppCompatActivity);
}

看完代码有人会发现,相对于MVC模式来说,代码不仅没有减少,反而增加了许多接口,看起来有点晕,但仔细观察可以看到MVP的结构是非常清晰的。

下面我们仔细分析一下

MainActivity 实现了MainView接口,并实现了 onNext(..) , onError(..),showLoadingDialog(..)和dialogDissmiss(..) 这4个方法,但是这4个方法看起来好像都没有被调用,只是在 onCreate() 的时候创建了一个 MainPresenterImpl 对象,然后在 onDestroy() 的时候调用了 mPresenter.destroy() 方法, 在onCreate()里调用了 mainPresenter.onResume(..) 方法,那么既没有回调也没有直接调用,那 MainView 中的4个接口方法又是何时何地被调用的呢?接下来我们将继续分析 Presenter 层的实现代码。

在 MainPresenterImpl 中实现了 MainPresenter 接口并实现了 onResume(..) loadMore(..) onDestory(..) 方法,在构造方法中有一个MainView的参数,这个对象是 MainView 的引用,这个对象可以是 Activity 或者是 Fragment 也可以是 MainView 接口的任何一个实现类,但对于 MainPresenterImpl 而言具体的 IView 到底是谁并不知道。在 MainPresenterImpl 中,在 onResume(..)方法中除了调用 Model 外和MainView 的方法外,其他方法都调用了MainView接口提供的方法,以此来对 View 层的 UI 呈现以及交互提醒做出相应的响应。而最后的 onDestory(..) 方法则是用于释放对 IView 的引用。

因此我们得出结论:

对于View而言

我需要一位主持者,当出现视图相关事件的响应或者生命周期的变化时,我需要告诉这位主持者,我想要做些什么。

我会提供一系列通用接口,以便于当主持者完成我的请求后,调用相应的接口告诉我这件事的结果。

我所有的请求都发给主持者,让他帮我做决定,但是这件事是怎么做的,我并不知道也不关心,我只是需要结果。

对于 Presenter 而言:

我接收到 View 的请求后找 Model 寻求帮助,等 Model 做完事情后通知我了,我在把结果告诉 View。

我只知道指挥 Model做事、告诉 View 显示数据,但我不干活。

我相当于一座桥,连接着 View 和 Model,他们谁也不认识谁,想要通信必须要通过我,如果没有我,他们两永远都不会认识。没错,我就是这么重要。

由于有 Presenter的存在,View层代码看起来非常清晰,每个方法都有他自己的功能职责,彼此之间并不会相互耦合而 Presenter 中的代码也是如此,每一个方法都只处理一件事,并不会做其他无相关的事情。另外我们观察到,在 MainActivity 中并没有直接对 MainPresenterImpl 进行持有,而是持有了一个 MainPresenter 对象;同样的在 MainPresenterImpl 也并没有直接持有 MainActivity 而是持有了一个 IView 对象。也就是说,凡是实现了 MainPresenter 便是 Presenter 层,凡是实现了 MainView 便是 View 层,这样就能很方便地变更业务逻辑或者进行单元测试。

本篇博客示例代码: https://github.com/jerryyh/MvpAndroidmaster

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值