浅谈MVP实现Android应用层开发

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。因为CSDN也支持MarkDown语法了,牛逼啊!

【工匠若水 http://blog.csdn.net/yanbober

背景

之所以要谈这个话题是因为你在开发App时可能会发现,Activity担负的责任非常之重,如果站在MVC框架角度看自己开发的App,一般xml布局文件科Activity的setContentView等充当了View角色,Activity其他代码充当了Controller角色,其他数据来源(数据库等)充当了Model角色。所以你会发现Activity违背单一职责原则,负担过重。同时如果要测试逻辑数据层会发现比较难编写测试用例。由此来构想,有没有一种解耦的方法呢?

有,那就是MVP框架。

MVP架构

MVP就是Model-View-Presenter。其实不用做太多解释,如下图所示你就能明白大致:

技术分享

如上图所示可以看见MVP模式需要具备如下三要素:

  1. 最左侧的View。也就是Android中的Activity。同时需要创建一个View的抽象接口View interface。需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合。
  2. 最右侧的Model。用来操做实际数据(譬如数据存储等)。有时也需要创建一个Model的抽象接口Model interface用来降低耦合。
  3. 中间的Presenter。作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

程序猿的纠结—MVP与MVC

看完上面两段,你可能会疑惑MVP,还有一个MVC,他两啥关系?

那再来看下MVC吧!

MVC架构

MVC框架认为软件可以分成如下三个部分:

  • 视图(View):用户界面。
  • 控制器(Controller):业务逻辑。
  • 模型(Model):数据保存。

如下图很直观的展示了MVC框架的核心:

技术分享

View传送指令到Controller,Controller完成业务逻辑后,要求Model改变状态,Model将新的数据发送到View,用户得到反馈。

MVP与MVC对比

这时候你会发现MVC与MVP的结构图都有很大区别,具体区别如下:

MVP架构:

View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。
Presenter与View的交互是通过接口来进行的。
通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。

MVC架构:

View可以与Model直接交互。
Controller是基于行为的,并且可以被多个View共享。
可以负责决定显示哪个View。

总结

MVC与MVP很相似,但又有很大区别,站在不同分析角度会有不同的观点,这里只是站在基于Android App代码下来分析的结果。

MVP架构的Android应用实战:

完整工程代码点我进入下载页面

背景:如下案例模拟一次用户交互存取数据的过程,用户输入数据后点击保存,然后点击获取数据将保存的数据获取的操作。
UI界面:
技术分享

工程目录结构:
技术分享

详细代码:

首先看model层代码,model层提供抽象接口,方便解耦,同时方便测试用例测试model的impl实现代码。如下展示了抽象接口和实现代码。

Model层抽象接口:

public interface IInfoModel {
    //从数据提供者获取数据方法
    InfoBean getInfo();
    //存入数据提供者方法
    void setInfo(InfoBean info);
}

Model层抽象实现:

public class InfoModelImpl implements IInfoModel {
    //模拟存储数据
    private InfoBean infoBean = new InfoBean();

    @Override
    public InfoBean getInfo() {
        //模拟存储数据,真实有很多操作
        return infoBean;
    }

    @Override
    public void setInfo(InfoBean info) {
        //模拟存储数据,真实有很多操作
        infoBean = info;
    }
}

接着看View层代码,View层同样提供抽象接口,方便解耦,同时方便测试用例测试View的impl实现交互代码。如下展示了抽象接口的代码。

View层的抽象接口:

public interface IInfoView {
    //给UI显示数据的方法
    void setInfo(InfoBean info);
    //从UI取数据的方法
    InfoBean getInfo();
}

这个时候其实你可以想想,写UI和逻辑的人可以完全分工,他们通过接口对接。Presenter的角色更像是一个设计模式的适配器类,负责对接UI与数据逻辑。所以不妨先看下Presenter的实现:

public class Presenter {
    private IInfoModel infoModel;
    private IInfoView infoView;

    public Presenter(IInfoView infoView) {
        this.infoView = infoView;

        infoModel = new InfoModelImpl();
    }
    //供UI调运
    public void saveInfo(InfoBean bean) {
        infoModel.setInfo(bean);
    }
    //供UI调运
    public void getInfo() {
        //通过调用IInfoView的方法来更新显示,设计模式运用
        //类似回调监听处理
        infoView.setInfo(infoModel.getInfo());
    }
}

这时候回过头看下View层代码,View层同样实现了View层提供的抽象接口(也就是Activity类,充当UI View)。

如下展示了View层的实现代码:

public class MainActivity extends ActionBarActivity implements IInfoView, View.OnClickListener{
    private EditText inputId, inputName, inputAddr;
    private Button saveBtn, loadBtn;
    private TextView infoTxt;

    private Presenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();
    }

    private void initData() {
        presenter = new Presenter(this);

        inputId = (EditText) findViewById(R.id.id_input);
        inputName = (EditText) findViewById(R.id.name_input);
        inputAddr = (EditText) findViewById(R.id.addr_input);
        saveBtn = (Button) findViewById(R.id.input_confirm);
        loadBtn = (Button) findViewById(R.id.get_confirm);
        infoTxt = (TextView) findViewById(R.id.show);

        saveBtn.setOnClickListener(this);
        loadBtn.setOnClickListener(this);
    }

    @Override
    public void setInfo(InfoBean info) {
        StringBuilder builder = new StringBuilder("");
        builder.append(info.getId());
        builder.append("\n");
        builder.append(info.getName());
        builder.append("\n");
        builder.append(info.getAddress());

        infoTxt.setText(builder.toString());
    }

    @Override
    public InfoBean getInfo() {
        InfoBean info = new InfoBean();
        info.setId(Integer.parseInt(inputId.getText().toString()));
        info.setName(inputName.getText().toString());
        info.setAddress(inputAddr.getText().toString());
        return info;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.input_confirm:
                presenter.saveInfo(getInfo());
                break;
            case R.id.get_confirm:
                presenter.getInfo();
                break;
        }
    }
}

到此解耦并行开发的App编码工作已经完成,如下展示运行结果:

技术分享

完整工程代码点我进入下载页面

总结

通过上面的例子可以发现,View(Activity)只负责处理用户交互,并把数据相关的逻辑操作都交给了Presenter去做,而Presenter调用Model处理完数据之后,再通过View的抽象接口更新View显示的信息。这样就实现了完整的解耦UI与逻辑操作。

不过,这种观点也是站在App局部代码的角度来分析看待的,对于小型App完全没这个必要,大型的App和交互复杂的可以考虑这么处理,既可以解耦,还可以方便编写test测试代码。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。