MVP 架构实战深入浅出

本文为菜鸟窝作者 吴威龙 连载

菜鸟窝是专业的程序猿在线学习平台,提供最系统的 Android 项目实战课程

如需转载,请联系菜鸟窝公众号(cniao5),并注明出处。

MVP 架构实战深入浅出_第1张图片

前言

上一篇文章
RecycleView 综合使用案例(结合 ButterKnife、Retrofit、Picasso) 分享了在使使用 RecycleView 的时候如何结合 ButterKnife、Retrofit、Picasso 等框架进行使用。现在来聊聊 MVC 以及 MVP 框架吧。

下面来看看 MVC 的介绍:

MVC简介

MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

其中 M 层处理数据,业务逻辑等;V 层处理界面的显示结果;C 层起到桥梁的作用,来控制 V 层和 M 层通信以此来达到分离视图显示和业务逻辑层。

Android 中的 MVC

  • 视图层(View)

一般采用 XML 文件进行界面的描述,这些 XML 可以理解为 AndroidApp 的 View。使用的时候可以非常方便的引入。同时便于后期界面的修改。逻辑中与界面对应的 id 不变化则代码不用修改,大大增强了代码的可维护性。

  • 控制层(Controller)

Android 的控制层的重任通常落在了众多的 Activity 的肩上。这句话也就暗含了不要在 Activity 中写代码,要通过 Activity 交割 Model 业务逻辑层处理,这样做的另外一个原因是 Android 中的 Activity 的响应时间是 5s,如果耗时的操作放在这里,程序就很容易被回收掉。

  • 模型层(Model)

我们针对业务模型,建立的数据结构和相关的类,就可以理解为 AndroidApp 的 Model,Model 是与 View 无关,而与业务相关的。对数据库的操作、对网络等的操作都应该在 Model 里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。

MVC MVP 对比

MVP 架构实战深入浅出_第2张图片
image

通过分析上图,只需知道 MVC 传统模式是没有把 View 和 Model 层隔离开的,MVP 模式则是 View 层和 Model 完全解耦开,通过 Presenter 这个中间人进行传递信息。

下面看看 MVP 的介绍:

MVP

MVP 架构实战深入浅出_第3张图片
image

View:负责绘制 UI 元素、与用户进行交互(在 Android 中体现为 Activity)

Model:负责存储、检索、操纵数据(有时也实现一个 Model interface 用来降低耦合)

Presenter:作为 View 与 Model 交互的中间纽带,处理与用户交互的负责逻辑。

View interface:需要 View 实现的接口,View 通过 View interface 与 Presenter 进行交互,降低耦合,方便进行单元测试

一句话解释就是:Presenter 是 View 和 Model 之间的代理。

MVP 优点

  1. 降低耦合度,实现了 Model 和 View 真正的完全分离,可以修改 View 而不影响 Modle
  2. 模块职责划分明显,层次清晰
  3. 隐藏数据
  4. Presenter 可以复用
  5. 利于测试驱动开发
  6. View 可以进行组件化
  7. 代码灵活性

MVP缺点

  1. Presenter 中除了应用逻辑以外,还有大量的 View->Model,Model->View 的手动同步逻辑,造成 Presenter 比较笨重,维护起来会比较困难。

  2. 由于对视图的渲染放在了 Presenter 中,所以视图和 Presenter 的交互会过于频繁。

  3. 如果 Presenter 过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么 Presenter 也需要变更了。

  4. 额外的代码复杂度及学习成本。

代码实现:

MVP 是一种思想,每个人的理解不一样,所以每个人的实现都是大同小异的。谷歌推出官方的 MVP Demo,我们可以参考谷歌给出的进行稍微修改一点点,形成自己风格的 mvp 模式。注意,模式的实现是没有所谓的标准的,只要达到这种解耦效果就可以了。

这里以 菜鸟手机助手 的【推荐】栏目举例说明

模块解析:

  • Contract 接口:里面定义 presenter 接口 和 view 接口

  • presenter :负责和 view,module 交互

  • view :基本都是对控件进行更新即可

BaseView

public interface BaseView {

    //声明公共的一些方法
    void showLodading();//显示加载进度条
    void dimissLoading();//关闭加载进度条
}

RecommendContract

该类存放两个接口,View 接口和 Presenter 接口。
以前两个接口都是分开写的,现在合起来放在一个接口类里面,显得不那么凌乱了。

接口 View 给具体视图层实现,譬如本例中的 RecommendFragment。

接口 Presenter 给具体的 Presenter 层实现,譬如本例的 RecommendPresenter。


public interface RecommendContract {

    //接口与接口之间的继承是用 extends
    interface View extends BaseView{

        void showResult(List datas);  //显示数据
        void showNodata();                     //提示没数据
        void showError(String msg);            //提示错误
    }
    
    // BasePresenter 暂时为空,就不列代码出来了,以后可以增加
    interface  Presenter extends BasePresenter{

        public void requestDatas();//请求数据
    }

}

Model 层

RecommendModel 类里面调用到的 HttpManager、ApiService 类的代码就不贴出来了,因为本例主要讲 MVP 模式。

public class RecommendModel {

    // presenter 层调用该方法,执行完毕有回调方法
    public  void getApps(Callback> callback){

        HttpManager manager = new HttpManager();

        ApiService apiService =manager.getRetrofit(manager.getOkHttpClient()).create(ApiService.class);

        apiService.getApps("{'page':0}").enqueue(callback);
    }
}

Presenter 层

实现 Contract 层接口 RecommendContract.Presenter,实现该接口下的抽象方法。

实现抽象方法:requestDatas(),请求数据

代码中 引用 model 层的对象RecommendModel mModel,
通过 mModel.getApps() 调用 Model 层的具体方法实现需求。

引用 view 接口实例对象,接口引用指向一个对象 RecommendContract.View mView,
通过 mView.showLodading()、mView.showNodata() 等调用 View 层具体方法实现需求。

public class RecommendPresenter implements RecommendContract.Presenter {

    //引用 model 层的对象
    private RecommendModel mModel;

    //引用 view 接口实例对象,接口引用指向一个对象
    private RecommendContract.View mView;

    //构造方法中传过来 view 对象
    public RecommendPresenter(RecommendContract.View view){

        this.mView = view;

        mModel = new RecommendModel();
    }


    //实现 RecommendContract.Presenter 接口,重写接口的抽象方法
    @Override
    public void requestDatas() {

        //调用 实现了RecommendContract.View 接口的  fragment 里面重写的 showLodading()方法
        mView.showLodading();

        mModel.getApps(new Callback>() {
            @Override
            public void onResponse(Call> call, Response> response) {

                if(response !=null){

                    mView.showResult(response.body().getDatas());
                }
                else{
                    mView.showNodata();
                }

                mView.dimissLoading();
            }
            @Override
            public void onFailure(Call> call, Throwable t) {
                mView.dimissLoading();
                mView.showError(t.getMessage());
            }
        });
    }
}

View 层

实现 Contract 层接口 RecommendContract.View,实现该接口下的几个抽象方法:
showLodading(),
dimissLoading(), showNodata(), showError(), showResult()

代码中使用 RecommendContract.Presenter mPresenter 接口实例对象,接口引用指向一个对象,
通过 mPresenter.requestDatas() 调用 Presenter 的 requestDatas()方法。
以此达到解耦的目的。

public class RecommendFragment extends Fragment  implements RecommendContract.View {

    @BindView(R.id.recycle_view)
    RecyclerView mRecyclerView;

    private RecomendAppAdatper mAdatper;

    private ProgressDialog mProgressDialog;

    private RecommendContract.Presenter mPresenter;//presenter接口实例对象,接口引用指向一个对象

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {


        View view = inflater.inflate(R.layout.fragment_recomend, container, false);
        ButterKnife.bind(this, view);

        mProgressDialog = new ProgressDialog(getActivity());

        //实例化 Presenter 
        mPresenter = new RecommendPresenter(this);

        initData();
        return view;

    }
    private void  initData(){

        //调用 presenter 去请求数据,实际上,presenter 是指挥 Model 去做实际操作
        mPresenter.requestDatas();
    }

    //数据显示
    private void initRecycleView(List datas){

        //为RecyclerView设置布局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        //为RecyclerView设置分割线(这个可以对DividerItemDecoration进行修改,自定义)
        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));

        //动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mAdatper = new RecomendAppAdatper(getActivity(),datas);

        mRecyclerView.setAdapter(mAdatper);
    }

    // 实现 RecommendContract.View  接口后  重写的抽象方法
    @Override
    public void showResult(List datas) {
        initRecycleView( datas);
    }

    @Override
    public void showNodata() {

        Toast.makeText(getActivity(),"暂时无数据,请吃完饭再来",Toast.LENGTH_LONG).show();
    }
    @Override
    public void showError(String msg) {
        Toast.makeText(getActivity(),"服务器开小差了:"+msg,Toast.LENGTH_LONG).show();
    }

    @Override
    public void showLodading() {

        mProgressDialog.show();
    }

    @Override
    public void dimissLoading() {

        if(mProgressDialog.isShowing()){
            mProgressDialog.dismiss();
        }
    }
}

总结

通过上面代码分析可以很清晰的明白 MVP 框架模式是怎么进行代码解耦的。下面再次梳理一下 MVP 实现步骤:

  • 定义 Presenter、View 接口(可以向上面例子,放在 Contract 接口里面):接口里面定义 presenter 层、view 层的抽象方法。简单的说就具体实现类所要实现的方法。

  • 具体 Presenter 层实现类实现定义的 Presenter 接口,实现该接口的抽象方法。
    比如 RecommendPresenter 类实现了 RecommendContract.Presenter 接口,实现 requestDatas()

  • 具体 View 层实现类实现定义的 View 接口,实现该接口下的抽象方法。
    比如 RecommendFragment.

  • RecommendFragment 通过类中的 RecommendPresenter 对象调用 Presenter 层里面的方法:requestDatas() 方法。在 Presenter 层中通过 Model 对象调用 Model 层里面的具体方法:getApps(). Model 层的方法执行完后通过回调把结果回调给 Presenter 层,Presenter 层再通过 View 层的接口实例对象,调用相关方法把结果回调到具体实现了该 View 接口的 View 层。

撸这个项目的一半,你就是大神 , 戳http://mp.weixin.qq.com/s/ZagocTlDfxZpC2IjUSFhHg

你可能感兴趣的:(MVP 架构实战深入浅出)