MVC/MVP/MVVM浅析
前言
随着项目的需求增加,代码的堆积会造成整个程序臃肿不堪。那么就急切需要了解些实用的架构设计,优化代码结构以及增加扩展性。本文将介绍MVC
、MNP
、MVVM
以及简要介绍下MVVM
要使用到的DataBinding
双向绑定。
MVC
架构
Android原生开发采用XML文件实现页面布局,在Activity中利用Java编写业务逻辑,这种开发模式实际上已经采用了MVC
的思想,分离视图和控制器。MVC
模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。如下图所示:
- 视图层(View):通常是Android的XML文件。
- 控制层(Controller):Activity,Fragment等。
- 模型层(Model):与数据相关的操作,如对数据库的操作、网络数据的获取等。
但实际上很多时候,Activity/Fragment也承担了视图层的部分功能。比如说有时候控件根据需求动态显示背景或者显示隐 藏等。也就是说控制层和视图层没有很好的解耦,结果就会如下图所示。
MVC
代码Demo
定义一个Activity也就是控制层,当用户点击的时候将自己和Model层绑定并请求数据。
public interface OnResultListener {
void onSuccess(String infor);
}
public class MainActivity extends Activity implements OnResultListener, View.OnClickListener {
private DataModel dataModel;
private TextView result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataModel = new DataModel();
}
@Override
public void onClick(View v) {
dataModel.getResult(this);
}
@Override
public void onSuccess(String infor) {
result.setText(infor);
}
}
模型层收到数据后,做耗时操作。耗时操作完毕,返回数据更新视图层。
public interface DataModel {
void getResult(OnResultListener listener);
}
public class DataModelImpl implements DataModel {
@Override
public void getResult(final OnResultListener listener) {
//耗时操作
listener.onSuccess("Successed");
}
}
MVP架构
为了解决MVC
中控制层和视图层的耦合。把控制层中的业务逻辑抽取出来独立成Presenter,把Activity和XML一起称之为视图层。此时视图层的Activity并不持有模型层对象,模型层数据也并不会直接流向视图层。他们之间需要Presenter协调,Presenter持有两者的接口。
这样做的避免了MVC中控制层的过度臃肿同时也清晰了各层的职责。
MVP
代码Demo
定义一个Activity实现IView接口,然后把自己作为借口对象传入presenter对象中。也就是说对与Presenter来说UI的事情就不管了,调用接口就可以。
这样子就是实现了将业务逻辑从Activity抽离出来。Presenter只要负责业务逻辑就可以,Activity+XML只要负责UI就可以,彼此透明。
public interface IView {
void setText(String infor);
}
public class MainActivity extends Activity implements IView, OnClickListener {
private Presenter presenter;
private TextView result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new Presenter(this);
}
@Override
public void onClick(View v) {
presenter.getResult();
}
@Override
public void setText(String infor) {
result.setText(infor);
}
}
public class Presenter {
IView view;
DataModel dataModel;
public void Presenter(IView view) {
this.view = view;
dataModel = new DataModel();
}
public void getResult() {
view.setText(dataModel.getResult());
}
}
模型层收到数据后,做耗时操作。耗时操作完毕并不是直接更新UI,而是经Presenter中转给视图层更新UI。
注意此时的Presenter与XML并没有联系,而之前MVC架构的Activity与XML布局是直接关联在一起的!
public interface DataModel {
void getResult(OnResultListener listener);
}
public class DataModelImpl implements DataModel {
@Override
public String getResult() {
//耗时操作
return "Successed";
}
}
MVVM
架构
MVP虽好,那么问题来了
假设一个应用的界面拥有3个Actvity
那么意味着3个View接口供Present使用。
有9个数据来刷新界面,也就是对应着接口的15个控件操作方法。
假设他们是:
public interface IView1 {
void ViewFunction1(String data1);
void ViewFunction2(String data2);
void ViewFunction3(String data3);
}
public interface IView2 {
void ViewFunction4(String data4);
void ViewFunction5(String data5);
}
public interface IView3 {
void ViewFunction6(String data5);
}
那么如果有一天增加了场景。该场景有两个Activity,一样ViewFunction方法而对应9个数据不变。
那么假设接口变成了以下两个:
public interface INewView1 {
void ViewFunction1(String data1);
void ViewFunction3(String data3);
void ViewFunction4(String data4);
void ViewFunction5(String data5);
}
public interface INewView2 {
void ViewFunction2(String data2);
void ViewFunction5(String data5);
}
那么由于随着Activity+XML
的变更,Presenter
不是也要跟着增加?
如果有几十个上百个Activity+XML
需要修改,那么对应的Presenter
不是也要修改。
那为何不将data
和对应控件直接绑在一起这样就摆脱了接口的束缚。
MVVM
是Model-View-ViewModel
的简写.
它是有三个部分组成:Model
、View
、ViewModel
。
Model
:数据模型层。
View
:layout
、views
。
ViewModel
:View
和Model
之间的链接桥梁,同步两边的变化。
可以看到重点在于ViewModel
的双向绑定功能,对于双向绑定,刚好可以使用DataBinding
,DataBinding
是一个实现数据和UI
绑定的框架,是构建MVVM
模式的一个关键的工具。
DataBinding
使用
1、在build.gradle
中设置
dataBinding {
enabled = true
}
2、创建实体类
public class ObservableObjectsUser extends BaseObservable {
private String Value;
public ObservableObjectsUser() {
}
public ObservableObjectsUser(String value) {
this.value = value;
}
@Bindable
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
notifyPropertyChanged(BR.value);
}
}
3、XML布局绑定数据
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.yl.databindingdemo.bean.ObservableObjectsUser" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
//双向绑定
android:text="@={user.value}" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.value}" />
</LinearLayout>
</layout>
4、数据绑定XML布局
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDoubleBindingBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_binding);
mObservableObjectsUser = new ObservableObjectsUser();
binding.setUser(mObservableObjectsUser);
}
OK!绑定完成。看下运行效果: