Android MVC和MVP架构的详解
用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面
及用户交互的同时,不需要重新编写业务逻辑。其中
M层:处理数据,业务逻辑等;
V层:处理界面的显示结果;
C层:起到桥梁的作用,来控制V层和M层通信以此来达到分离视图、显示和业务逻辑层。
在Android中使用如下图:
View 层:在开发中xml文件就是我们的View布局,一般情况下我们都会用xml来实现我们的布局,相对的节省性能的消耗。但是也有一些比较特殊、比较复杂的需求我们需要用到自定义view,其实自定义View也是属于View,这是比较少的情况,正常我们都会用xml来实现View的。
Controller层:控制层,其实Android已经默认给我们提供了Controller,就是Activity和Fragment,仔细一想就可以明白,用户在View层做出请求的操作,Activity和Fragment就会对用户做出的请求做出相应的处理,得到相应的结果之后,再呈现给用户。
其实对于Activity和Fragment人们有时会认为它是属于View层的。的确,在MVC的View层中的xml布局是不会改变的,而当用户在操作的时候我们的View总要做出相应的改变,而有些改变单单是xml是没办法做到的,所以在Activity和Fragment这个Controller层中多多少少都会有View层的东西。但是在Activity和Fragment中总归还是Controller层的代码占绝大多数,因此我们还是把它归类为Controller层。
Model层:Android中除了View和Controller,剩下的绝大多数来说都是Model层的东西了。其实Model一般不会被定义得很死,Model本身就数据和业务逻辑有关的,而不同的需求当然是有不同的数据和业务逻辑的,其中数据模型(javaBean)、数据的存取、网络的操作、以及一些耗时的任务等等。
项目结构:
接下来Demo演示,数据模型类:
public class Person {
private int per_id;
private String username;
private String sex;
private int age;
public Person(int per_id, String username, String sex,
int age) {
this.per_id = per_id;
this.username = username;
this.sex = sex;
this.age = age;
}
public int getPer_id() {
return per_id;
}
public void setPer_id(int per_id) {
this.per_id = per_id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person->[per_id=" + per_id + ", username=" + username
+ ", sex=" + sex + ", age=" + age
+ "]";
}
}
获取数据侦听:
public interface GetDataListener {
void onGetDataSuccess(Person person);
void onGetDataError(String errmessage);
}
获取数据接口:
public interface GetData {
void getShopsData(GetDataListener getDataListener);
}
获取数据:
public class GetDataImplement implements GetData {
@Override
public void getShopsData(GetDataListener getDataListener) {
Person person = new Person(1, "layne", "male", 18);
if(person!=null){
getDataListener.onGetDataSuccess(person);
}else{
getDataListener.onGetDataError("请求失败");
}
}
}
控制层:
public class MainActivity extends ActionBarActivity implements GetDataListener,
OnClickListener {
private Button bt_request;
private TextView tv_content;
private GetData getData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitView();
}
private void InitView() {
bt_request = (Button) findViewById(R.id.bt_request);
tv_content = (TextView) findViewById(R.id.tv_content);
bt_request.setOnClickListener(this);
getData = new GetDataImplement();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_request:
getData.getShopsData(this);
break;
}
}
@Override
public void onGetDataSuccess(Person person) {
showData(person);
}
@Override
public void onGetDataError(String errmessage) {
Toast.makeText(this, errmessage, Toast.LENGTH_SHORT).show();
}
// 控制层将Model层里面的数据呈现到View层
private void showData(Person person) {
tv_content.setText(person.toString());
}
}
视图层:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.itman.mvcdemo1.controller.MainActivity" >
<Button
android:id="@+id/bt_request"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="请求数据" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="请求的内容" />
</LinearLayout>
</LinearLayout>
运行结果:
总结:
MVC的优点:
很明显如果要求不高,我们平时的小项目就是一般的MVC,这个框架学习成本低,理解起来非常容易,对UI层与业务层进行分离,对Model层和数据方面做一下分层和封装,就是一个扩展性不错,挺清晰的MVC开发框架了。
但是缺点也是很明显的:
随着我们的业务壮大起来,需求越来越多,那么Activity和Fragment的代码就会越来越多,有的甚至夸张到一个activity就有几千行代码的,就会变得越来越臃肿,当我们维护和改变需求的时候是非常耗时间和精力的。这个就是MVC的缺点。
MVP架构
根据上文一开始MVC架构,项目随着需求不断增多,业务逻辑更加的复杂,我们的activity和fragment的代码也是越来越臃肿的。这时的activity和fragment理解和维护起来科室非常的费事费力,很痛苦的一件事,相信我们程序员都不太愿意去面对这样的情况的。这时MVP就应运而生,原先MVC中的Controller层activity或fragment将抽出控制层的代码,剩余的归类为View层,也就是说activity和fragment在MVP中是属于View层的,而被抽出的控制层代码成为了新的一员Presenter。
MVP的各个职责:
View层:对应于Activity,负责View的绘制以及与用户交互,处理界面的显示结果
Model层:处理数据,业务逻辑等
Presenter层:负责完成View于Model间的交互
MVP其实就是减少了Activity和fragment的职责,减少了Activity和fragment的代码,将繁杂的逻辑代码交给了Presenter,相对应的好处就是耦合度更加的低,更方便单元测试了,同时借用两张图来说明变化(图片出处):
转变为:
MVC和MVP的区别(图片出处):根据图中我们可以知道在MVC中Model层是可以和View层交互的,但是在MVP中Model层和View层的交互是完全交给了Presenter的
接下来我们根据上面的MVC的Demo,将之修改成MVP架构的Demo
项目结构
数据模型:
public class Person {
private int per_id;
private String username;
private String sex;
private int age;
public Person(int per_id, String username, String sex,
int age) {
this.per_id = per_id;
this.username = username;
this.sex = sex;
this.age = age;
}
public int getPer_id() {
return per_id;
}
public void setPer_id(int per_id) {
this.per_id = per_id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person->[per_id=" + per_id + ", username=" + username
+ ", sex=" + sex + ", age=" + age
+ "]";
}
}
请求侦听:
public interface GetDataListener {
void onGetDataSuccess(Person person);
void onGetDataError(String errmessage);
}
Presenter接口:
public interface IGetDataPresenter {
void getData(GetDataListener getDataListener);
void clear();
}
Presenter:
public class GetDataPresenterCompl implements IGetDataPresenter {
private IGetDataView getDataView;
private Person person;
public GetDataPresenterCompl(IGetDataView getDataView) {
this.getDataView = getDataView;
}
@Override
public void getData(GetDataListener getDataListener) {
person = new Person(1, "layne", "male", 18);
if (person != null) {
getDataListener.onGetDataSuccess(person);
} else {
getDataListener.onGetDataError("请求失败");
}
}
@Override
public void clear() {
getDataView.clearData();
}
}
视图的接口:
public interface IGetDataView {
void clearData();
}
Activity:
public class MainActivity extends ActionBarActivity implements IGetDataView,
GetDataListener, OnClickListener {
private Button bt_request, bt_clear;
private TextView tv_content;
private IGetDataPresenter getDataPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitView();
}
private void InitView() {
bt_request = (Button) findViewById(R.id.bt_request);
bt_clear = (Button) findViewById(R.id.bt_clear);
tv_content = (TextView) findViewById(R.id.tv_content);
bt_request.setOnClickListener(this);
bt_clear.setOnClickListener(this);
getDataPresenter = new GetDataPresenterCompl(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_request:
getDataPresenter.getData(this);
break;
case R.id.bt_clear:
getDataPresenter.clear();
break;
}
}
@Override
public void onGetDataSuccess(Person person) {
showData(person);
}
@Override
public void onGetDataError(String errmessage) {
Toast.makeText(this, errmessage, Toast.LENGTH_SHORT).show();
}
@Override
public void clearData() {
tv_content.setText("清空内容");
}
private void showData(Person person) {
tv_content.setText(person.toString());
}
}
最后是布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.itman.mvpdemo.view.MainActivity" >
<Button
android:id="@+id/bt_request"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:text="请求数据" />
<Button
android:id="@+id/bt_clear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:text="清除数据" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="请求的内容" />
</LinearLayout>
</LinearLayout>
运行结果:
MVP的优点:
1.更加的降低了耦合度,使得Model层和View层的完全解耦
2.模块职责划分清楚,层次分明
3.Presenter一定程度下可以复用,一个Presenter可用于多个View,前提是View的改动不影响业务逻辑。
4.单元测试更加的方便
缺点:
1.层次分明的代价是额外的代码和类,这更加的增加了学习成本
2.如果Presenter过多地与特定的视图的联系过于紧密,一旦视图需要变更,那么Presenter也需要变更了。