MVP这种设计模式在Android领域越来越火,经常会听到某某Android应用采用了MVP+Retrofit+RxJava的架构,甚至很多工程师都说面试过的人喜欢提MVP,却并不知道它真正的机制。那么,究竟什么是MVP模式,它又与MVC模式有什么异同呢?
MVP与MVC
相信对于大部分开发者而言,MVC是一种非常熟悉的模式,它广泛的应用于web等诸多常见的软件中。MVC将整个工程分为三个部分,Model(模型)、View(视图)、Controller(控制器)中,对于以往常用的Android项目而言,更多的人们会将布局文件作为View,而将Activity(或Fragment,下同)作为Controller。
然而,在Activity中实际上做了很多的事情,不仅处理了业务逻辑,有时也会对UI进行操作,这样就使得Activity看起来既像Controller层又像View层,随着项目的不断发展,这些Activity和Fragment就会变得越来越臃肿,难以扩展和维护,因此便出现了MVP模式。
作为MVC的演化版本,在MVP模式中,将整个工程分为了Model(模型)、View(视图)、Presenter(表示器),与MVC的异同见下图
在图中我们能够看出,MVP与MVC最大的不同就是在于Model与View并不能直接交互,而是隔着一层Presenter,相比于MVC,MVP中的Model会更少的关注业务逻辑,与View的耦合度也进一步降低,这种模式不仅使得代码逻辑更加清晰,同时也能减少我们部署以及单元测试的时间。
Demo
网上很多例子都是使用的用户登录(当然也有很多比较复杂的),所以实现了一个其他的功能,主要用于说明各模块之间的关系。
demo实现的效果如下图,主要功能为输入一个字符串,点击按钮将它变成大写并且输出
在这个demo中的java代码结构如下图:
- Activity用于展示内容
- biz是业务逻辑层,用于避免在Model中进行逻辑处理
- model是模型层
- presenter是表示器层,用于处理Model和View之间的交互
- view中封装了一些接口,和activity共同组成了视图层
(1)Model层
在Model层中,我们定义了一个Model,名为MyModel
package com.example.steveyg.androiddemo.model;
/**
* Created by steveyg on 2016/12/20.
*/
public class MyModel {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
一般很少有实体只包含一个属性,不过此处用于举例就写了一个简单的实体类。接着定义了相应的biz
package com.example.steveyg.androiddemo.biz;
/**
* Created by steveyg on 2016/12/20.
*/
public interface IMyBiz {
public void exec(String str, OnExecListener listener);
}
package com.example.steveyg.androiddemo.biz;
import com.example.steveyg.androiddemo.model.MyModel;
/**
* Created by steveyg on 2016/12/20.
*/
public interface OnExecListener {
void execSuccess(MyModel model);
}
package com.example.steveyg.androiddemo.biz;
import com.example.steveyg.androiddemo.model.MyModel;
/**
* Created by steveyg on 2016/12/20.
*/
public class MyBiz implements IMyBiz {
@Override
public void exec(final String str, final OnExecListener listener) {
new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyModel model = new MyModel();
String result = str.toUpperCase();
model.setStr(result);
listener.execSuccess(model);
}
}.run();
}
}
在实现IMyBiz接口的时候,采用了异步的操作,因为大部分需要处理数据的时候都是要和数据库或服务器交互的。
(2)View层
在View层中,首先定义了一个布局文件,里面有一个提示语,一个输入框和一个按钮:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http:///apk/res/android"
xmlns:tools="http:///tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.steveyg.androiddemo.activity.MainActivity">
<TextView
android:layout_alignTop="@+id/edit"
android:layout_alignBottom="@+id/edit"
android:gravity="center"
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:text="TEXT : " />
<EditText
android:id="@+id/edit"
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/submit"
android:text="submit"
android:layout_marginTop="10dp"
android:layout_below="@+id/edit"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
接着,我们定义一个接口IMyView,里面定义了视图层需要进行的操作:
package com.example.steveyg.androiddemo.view;
import com.example.steveyg.androiddemo.model.MyModel;
/**
* Created by steveyg on 2016/12/20.
*/
public interface IMyView {
public String getStr();
public void showSuccess(MyModel model);
}
getStr()用来获取输入框中的文字,showSuccess()用于展示最后的结果。接着,在Activity中实现相应的方法
package com.example.steveyg.androiddemo.activity;
import .AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.steveyg.androiddemo.R;
import com.example.steveyg.androiddemo.model.MyModel;
import com.example.steveyg.androiddemo.presenter.MyPresenter;
import com.example.steveyg.androiddemo.view.IMyView;
public class MainActivity extends AppCompatActivity implements IMyView {
private EditText mEditText;
private Button mButton;
private MyPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
@Override
public String getStr() {
return mEditText.getText().toString();
}
@Override
public void showSuccess(MyModel model) {
Toast.makeText(this,model.getStr(),Toast.LENGTH_SHORT).show();
}
private void init(){
mPresenter = new MyPresenter(this);
mEditText = (EditText)findViewById(R.id.edit);
mButton = (Button)findViewById(R.id.submit);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.exec();
}
});
}
}
(3)Presenter层
Presenter层主要是让View和Model进行交互,因此我们定义一个执行相应功能的方法:
package com.example.steveyg.androiddemo.presenter;
import android.os.Handler;
import com.example.steveyg.androiddemo.biz.IMyBiz;
import com.example.steveyg.androiddemo.biz.MyBiz;
import com.example.steveyg.androiddemo.biz.OnExecListener;
import com.example.steveyg.androiddemo.model.MyModel;
import com.example.steveyg.androiddemo.view.IMyView;
/**
* Created by steveyg on 2016/12/20.
*/
public class MyPresenter {
private IMyBiz myBiz;
private IMyView myView;
private Handler mHandler = new Handler();
public MyPresenter( IMyView view) {
this.myBiz = new MyBiz();
this.myView = view;
}
public void exec() {
myBiz.exec(myView.getStr(), new OnExecListener() {
@Override
public void execSuccess(final MyModel model) {
//通过Handler在UI线程中处理
mHandler.post(new Runnable() {
@Override
public void run() {
myView.showSuccess(model);
}
});
}
});
}
}
至此,一个简单的MVP demo就完成啦,不难看出,相比于MVC模式,代码量增加了很多,但是逻辑更加清晰,在实际的项目中,还是要好好设计下整个项目的结构,而且选定一种模式最好能够坚持的使用下去,在中途切换模式,有可能会导致整个项目的重构。
参考资料
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0227/2503.html