美文网首页
Android中MVP小试牛刀

Android中MVP小试牛刀

作者: CarlosLynn | 来源:发表于2018-06-24 11:51 被阅读22次

    前言

    世代在进步,技术在发展,当自己刚换了一份工作的时候,越来越多的听到MVP,那么今天就趁此机会来学习一下MVP,对他进行一个彻底的了解

    MVP概述

    MVP其实同MVC一样,是一种编程模式和思想,也许更准确地讲是一种架构。

    MVP分析

    况且,随着项目的深入发展,很多逻辑很越来越复杂,Activity处理的东西也会越来越多,代码越来越臃肿。这样一来维护起来的代价就会越来越高,这是因为View的变化会引起Controller的很多变化,反之亦然。用一句大白话来说明就是–某一段代码的变动会引起很多其他相关联的代码的改动,而程序员都是懒惰的,所以会恨死这样的代码。
    而MVP就是要减轻在Android中的这种困惑。
    View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这就是MVP模式的整个核心过程。
    MVP是基于MVC的,它的架构图如下:


    image.png

    MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。

    • M(Model) 数据相关层,model是整个应用或界面的数据加工处理厂,所谓数据加工厂就是对数据的获取,数据的解析,数据的存储,数据的分发,数据的增删改查等操作。意思就是凡是涉及到数据操作都是在model进行的,所以model不仅仅只是实体类的集合,同时还包含关于数据的各种处理操作,负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;
    • V(View) 视图层,就是用户直接看到的界面,负责界面数据的展示,与用户进行交互;如Activity,一个view可以同时拥有多个presenter,也可以只有一个presenter
    • P(Presenter) 纽带层,首先presenter是处于mvp的中间层,将模型与视图分离开来,用来连接Model与View会把view交给自己的命令进行一定的校验等操作交给model处理,会把model处理的结果交给view,

    MVP开发在Android中的基本流程

    1. View层定义View.interface,用来定义View的行为。一般由Activity或者是Fragment来实现这个接口,它定义了View视图的各种变化,如设置Textview,加载对话框,更新进度条等。
    2. Model层定义Modle.interface,这个是用来定义数据层发生变化时的通知接口,因为Model不能直接与View交互,所以它与Presenter交互,然后再通过Presenter间接达到与View的交互。
    3. Presenter翻译的意思是主持人,也就是主持场合,控制节奏的意思。在这时Presenter就负责具体的业务逻辑,请求数据,把数据送到Model,或者监听Model的数据变化,接受View层的动作,负责通过通知View层的视图变化。

    如果跟MVC的架构图对比的话,可以发现它们有相似之处也有不同。

    相似之处

    模块划分的相似
    MVC由Model、View、Controller构成。
    MVP由Model、View、Presenter构成。

    不同的地方

    1. MVP中Presenter取代了MVC中的Controller
    2. MVC中Model、View、Controller之间相互发生通信,而MVP中Model与Presenter相互通信,View与Presenter相互通信,而Model与View之间没有通信。

    Android中MVP的好处?

    就Android层面上来讲MVC架构虽然好,但不是最好,情况前面有讲过。用一句话概括就是“模块界限很模糊”。而MVP的出现实际上就是将MVC进行升级

    1、学习过设计模式的人都知道,这样做基本符合了单一职责原则。
    2、符合单一职责原则后,导致类与类组织更清晰。
    3、对应Android开发中就是帮助Activity解压,View层与Model层交互需要通过Presenter层进行,这样v与m层级间的耦合性降低。
    4、MVC中Activity同时充当了V和C的角色,这就属于界限划分不清楚。而MVP则划分的很清楚,Activity只充当V的角色,业务逻辑控制交给了Presenter.通过这种分层处理,每一层的测试也相对简单,维护性更高。

    个人对MVP模式的理解

    这一段是我自己的看法,也许不正确。
    我个人觉得MVP没有什么很神秘的,因为Android SDK上开发,本来就差不多是MVC的角色。Activity基本上Android开发中最重要的一环。
    我以前在团队工作的时候,团队分工是每人负责相应的Activity,在这里Activity是最小的开发单元。再后来,某些Activity变得越来越重要,越来越复杂,代码也越来越多,这样会造成团队某个人的开发任务重,而其他的团队成员也帮不上忙。而MVP的出现可以将Activity再细分,划为View和Presenter两个部分,所以Activity不再是最小的开发单元,如果可以完全可以这样分配任务,一个开发人员负责View部分,另一个开发人员负责Presenter部分。
    况且因为MVP的划分,所以各个部分其实相对独立,V的变动会对P的部分造成较少的影响,而M对V或者说V对M几乎是透明的。
    因为Presenter的存在,View和Model就可以很轻松,顶多Presenter累一点。
    还有一个特点是MVP模式很适合测试,单独测试VIEW成了一种可能。我们可以模拟View和Model的数据来测试Presenter的逻辑。

    MVP实战

    所以,我用一个简单的DEMO来讲解,大家一看就明白。

    场景需求

    假设现在需要做一款APP,就是显示天气,界面很简单,一个TextView显示天气信息,一个Button用来请求实时天气。
    如下图所示


    image.png

    软件启动后,会自动获取天气,然后TextView就可以显示信息。而用户点击获取实时天气的按钮,界面上会弹出正在获取中的进度对话框,等待数据加载成功后,对话框消失。Textview显示就新的天气情况。


    1.gif

    代码开发,包的组织

    因为选定MVP模式,所以第一步就是包的组织。


    image.png

    View层的接口定义及实现

    在MVP中Activity用来专注视图的表现。
    而在本例子中View的表现有哪些呢?很多教程直接就上来贴代码,个人觉得这样是不好的。View的表现当然要用View.interface接口来定义
    现在我们来分析一下,在本例中View应该有哪些表现。
    1.显示天气信息
    接口方法定义

    void onInfoUpdate(String info);
    

    2.显示获取信息等待对话框
    接口方法定义

    void showWaitingDialog();
    

    3.取消显示对话框
    接口方法定义

    void dissmissWaitingDialog();
    

    最终View层的接口定义就完成了,完整代码如图所示:

    public interface IWetherView {
    
        void onInfoUpdate(String info);
    
        void showWaitingDialog();
    
        void dissmissWaitingDialog();
    }
    

    接口文件已经定义好了,那么View的实现呢?在这里用MainActivity去实现它。

    public class MainActivity extends AppCompatActivity implements IWetherView{
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        @Override
        public void onInfoUpdate(String info) {
    
        }
    
        @Override
        public void showWaitingDialog() {
    
        }
    
        @Override
        public void dissmissWaitingDialog() {
    
        }
    }
    

    具体的业务代码,我们等会再实现。
    Model层的接口定义及实现
    Model层是数据层,用来存储数据并且提供数据。在这里为了便于演示,数据被简化为了String类型。
    接口定义如下:

    public interface IWetherModel {
    
        //提供数据
        public String getInfo();
    
        //存储数据
        public void setInfo(String info);
    }
    

    它的实现文件如下:

    public class IWetherImpl implements IWetherModel {
    
        @Override
        public String getInfo() {
            return null;
        }
    
        @Override
        public void setInfo(String info) {
    
        }
    }
    

    Presenter代码及实现
    Presenter是个大忙人,因为要同时对View和Model对接,所以内部必须持有它们的接口引用。
    所以有如下:

    public class WetherPresenter {
        IWetherModel mModel;
        IWetherView mView;
    }
    

    Presenter与View的通信
    View—–>Presenter
    从视图界面出发,用户要请求数据,而Presenter是具体实现者,所以Presenter要提供方法代View的实现者调用,并且View的实现中必须要有Presenter的引用。
    所以MainActivity.java中要有WetherPresenter的引用。

    public class MainActivity extends AppCompatActivity implements IWetherView{
        ......
        WetherPresenter mPresenter;
        ......
    } 
    

    而Presenter也要开发API供View调用。
    所以Presenter要有requestWetherInfo()方法:

    public class WetherPresenter {
        IWetherModel mModel;
        IWetherView mView;
    
        //供View层调用,用来请求天气数据
        public void requestWetherInfo(){
    
        }
    }
    

    presenter—–>View
    presenter操作View,是通过View.interface,也就是View层定义的接口。
    所以很容易得到下面的代码:

    public class WetherPresenter {
    
        ......
        private void showWaitingDialog(){
            if (mView != null) {
                mView.showWaitingDialog();
            }
        }
    
        private void dissmissWaitingDialog(){
            if (mView != null) {
                mView.dissmissWaitingDialog();
            }
        }
    
        private void updateWetherInfo(String info){
            if (mView != null) {
                mView.onInfoUpdate(info);
            }
        }
        ......
    }
    

    因为Presenter持有View的引用,所以在这里要将View.interface注入到Presenter当中。

    public class WetherPresenter {
        IWetherModel mModel;
        IWetherView mView;
    
        ......
        public WetherPresenter(IWetherView mView) {
            this.mView = mView;
        }
        ......
    }
    

    Presenter与Model的通信
    Presenter与Model的通信也是双方的。
    Presenter—->Model
    presenter获取到了数据,可以交给Model处理

    private void saveInfo(String info){
            mModel.setInfo(info);
    }
    

    Model—–>Presenter
    Model处理完数据后它也能对Presenter提供数据。Presenter可以通过Model对象获取本地数据。

    WetherPresenter.java
    private String localInfo(){
    return mModel.getInfo();
    }

    Presenter代码实现
    前面已经讲了Presenter与Model,Presenter与View之间的通信,现在就可以编写代码将它们粘合起来。
    Presenter本身需要向服务器获取代码,所以还要编写它的相应方法:

    public void requestWetherInfo(){
            getNetworkInfo();;
        }
    
    private void getNetworkInfo(){
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                    try {
                        //打开等待对话框
                        showWaitingDialog();
                        //模拟网络耗时
                        Thread.sleep(6000);
    
                        String info = "21度,晴转多云";
                        //保存到Model层
                        saveInfo(info);
                        //从Model层获取数据,为了演示效果,实际开发中根据情况需要。
                        String localinfo = localInfo();
    
                        //通知View层改变视图
                        updateWetherInfo(localinfo);
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //取消对话框
                        dissmissWaitingDialog();
                    }
                }
            }).start();
        }
    

    到此,完整的Presenter代码如下:

    public class WetherPresenter {
        IWetherModel mModel;
        IWetherView mView;
    
        public WetherPresenter(IWetherView mView) {
            this.mView = mView;
            mModel = new IWetherModelImpl();
        }
    
        public void requestWetherInfo(){
            getNetworkInfo();;
        }
    
        private void showWaitingDialog(){
            if (mView != null) {
                mView.showWaitingDialog();
            }
        }
    
        private void dissmissWaitingDialog(){
            if (mView != null) {
                mView.dissmissWaitingDialog();
            }
        }
    
        private void updateWetherInfo(String info){
            if (mView != null) {
                mView.onInfoUpdate(info);
            }
        }
    
        private void saveInfo(String info){
            mModel.setInfo(info);
        }
    
        private String localInfo(){
            return mModel.getInfo();
        }
    
        private void getNetworkInfo(){
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                    try {
                        //打开等待对话框
                        showWaitingDialog();
                        //模拟网络耗时
                        Thread.sleep(6000);
    
                        String info = "21度,晴转多云";
                        //保存到Model层
                        saveInfo(info);
                        //从Model层获取数据,为了演示效果,实际开发中根据情况需要。
                        String localinfo = localInfo();
    
                        //通知View层改变视图
                        updateWetherInfo(localinfo);
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //取消对话框
                        dissmissWaitingDialog();
                    }
                }
            }).start();
        }
    }
    

    MainActivity代码编写
    生成Presenter。这个在Activity中的onCreate方法中,并把自身当成IWetherView注入到presenter当中。

    mPresenter = new WetherPresenter(this);
    

    2 . 操作Presenter。当用户点击按钮时,通过调用mPresenter获取数据,然后静待更新。

    mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mPresenter.requestWetherInfo();
                }
     });
    

    View.interface回调方法被触发时,进行相应的视图更新。
    这里主要的视图有
    显示对话框
    取消对话框
    显示 天气信息。
    对应代码如下:

    @Override
        public void onInfoUpdate(final String info) {
            Log.d(TAG, "onInfoUpdate: "+info);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mTvInfo.setText(info);
                }
            });
        }
    
        @Override
        public void showWaitingDialog() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(mDialog != null && mDialog.isShowing()){
                        mDialog.dismiss();
                    }
    
                    mDialog = ProgressDialog.show(MainActivity.this,"","正在获取中...");
                }
            });
    
        }
    
        @Override
        public void dissmissWaitingDialog() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(mDialog != null && mDialog.isShowing()){
                        mDialog.dismiss();
                    }
                }
            });
    
        }
    

    所以整个MainActivity.java代码如下:

    public class MainActivity extends AppCompatActivity implements IWetherView{
        private static final String TAG = "MainActivity";
    
        WetherPresenter mPresenter;
        private TextView mTvInfo;
        private Button mButton;
        private ProgressDialog mDialog;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mPresenter = new WetherPresenter(this);
    
            mTvInfo = (TextView) findViewById(R.id.tv_info);
            mButton = (Button) findViewById(R.id.btn_request);
            mButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mPresenter.requestWetherInfo();
                }
            });
        }
    
        @Override
        public void onInfoUpdate(final String info) {
            Log.d(TAG, "onInfoUpdate: "+info);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mTvInfo.setText(info);
                }
            });
        }
    
        @Override
        public void showWaitingDialog() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(mDialog != null && mDialog.isShowing()){
                        mDialog.dismiss();
                    }
    
                    mDialog = ProgressDialog.show(MainActivity.this,"","正在获取中...");
                }
            });
    
        }
    
        @Override
        public void dissmissWaitingDialog() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(mDialog != null && mDialog.isShowing()){
                        mDialog.dismiss();
                    }
                }
            });
    
        }
    }
    

    总结

    mvp非常适合大型的APP开发,越复杂它的优势越明显,但是如果APP代码本身很简明,mvp就有点绕弯子的感觉了。
    完整代码请点击
    参考代码
    SimpleNews

    相关文章

      网友评论

          本文标题:Android中MVP小试牛刀

          本文链接:https://www.haomeiwen.com/subject/uxtjyftx.html