Android日记之MVC、MVP和MVVM

前言 在项目的设计模式中,听到最多的就是MVC、MVP和MVVM这3个架构的设计模式了,也是经常面试会问到的设计模式,本篇文章将会通过一个小demo来进行代码实战来简单的讲解这3个设计模式的区别。


demo功能分析及编写 Android日记之MVC、MVP和MVVM
文章图片
图1
我将通过3种设计模式来实现此demo的编写,demo的功能是用户输入一个账户名称,然后通过查询按钮就可以查询到用户的等级信息。我们这时候可以分析一下需求。

Android日记之MVC、MVP和MVVM
文章图片
需求图,来源见参考,侵删
然后我们开始正式编写,首先建立一个账号的实体类和请求结果的回调接口,并且编写下布局。

//账号的实体类 public class Account {//名字 private String name; //账号等级 private int level; public String getName() { return name; }public void setName(String name) { this.name = name; }public int getLevel() { return level; }public void setLevel(int level) { this.level = level; } }//请求结果的回调接口 public interface ICallback {void onSuccess(Account account); void onFailed(); }


接着我们就开始正式编写了,在对应的Activity下输入以下代码。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private TextView textView; private EditText editText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化控件 initView(); }private void initView() { editText = findViewById(R.id.edit_text); textView = findViewById(R.id.txt_text); findViewById(R.id.btn_text).setOnClickListener(this); }@Override public void onClick(View v) {getAccountData(getUser(), new ICallback() { @Override public void onSuccess(Account account) { showSuccessPage(account); }@Override public void onFailed() { showErrorPage(); } }); }//获取用户输入的信息 private String getUser(){ return editText.getText().toString(); }//展示获取信息成功界面 private void showSuccessPage(Account account){ textView.setText("用户账号:"+ account.getName()+ "用户等级:"+ account.getLevel()); }//展示获取信息失败界面 private void showErrorPage(){ textView.setText("获取数据失败"); }//查询用户数据,模拟请求数据 public void getAccountData(String accountName, ICallback callback){ Random random = new Random(); boolean isSuccess = random.nextBoolean(); if (isSuccess){ Account account = new Account(); account.setName(accountName); account.setLevel(100); callback.onSuccess(account); }else { callback.onFailed(); } } }

从这段代码我们可以看出,如果不使用设计模式编写的话,发现Activity是又充当展示界面和业务逻辑,这样Activity会显的很臃肿,这时候我们就需要进行解耦了,也就是需要用到设计模式的时候了。


MVC的使用 MVC就是Model-View-Controller的缩写,即模型-视图-控制器。在Android中对应的话,可以看如图。

Android日记之MVC、MVP和MVVM
文章图片
对应如图,来源见参考,侵删
这就是一个MVC的比较常见的模型图,箭头代表事件的传递方向,这里注意一下,Model如果传递给View,我们一般不会让Model持有View的引用,我们可以使用注册监听来实现。说了这么多,我们接下来来优化一下代码。

Android日记之MVC、MVP和MVVM
文章图片
MVC各自负责的东西,来源见参考,侵删
刚刚说明了下MVC,在通过这个图,其实我们发现主要就是把查询账号数据的业务逻辑给分到了Model层,接着我们就创建一个Model类,把这个逻辑放进去。
public class MVCModel {//模拟请求数据 public void getAccountData(String accountName, ICallback callback){ Random random = new Random(); boolean isSuccess = random.nextBoolean(); if (isSuccess){ Account account = new Account(); account.setName(accountName); account.setLevel(100); callback.onSuccess(account); }else { callback.onFailed(); } } }

放到这里后怎么使用呢,其实很简单,我们直接让View层持有Model层的引用就可以使用这个逻辑了。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private TextView textView; private EditText editText; //让View层持有model层的引用 private MVCModel mvcModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); //实例化 mvcModel = new MVCModel(); }private void initView() { editText = findViewById(R.id.edit_text); textView = findViewById(R.id.txt_text); findViewById(R.id.btn_text).setOnClickListener(this); }@Override public void onClick(View v) {mvcModel.getAccountData(getUser(), new ICallback() { @Override public void onSuccess(Account account) { showSuccessPage(account); }@Override public void onFailed() { showErrorPage(); } }); }//获取用户输入的信息 private String getUser(){ return editText.getText().toString(); }//展示获取信息成功界面 private void showSuccessPage(Account account){ textView.setText("用户账号:"+ account.getName()+ "用户等级:"+ account.getLevel()); } //展示获取信息失败界面 private void showErrorPage(){ textView.setText("获取数据失败"); } }


Android日记之MVC、MVP和MVVM
文章图片
MVC结果如图
但其实这样也是有缺点的,虽然这一定程度上实现了Model和View的分离,降低了代码的耦合性,但是其实在Android中,Activity充当了Controller和View的责任,就是Activity即是控制器,又要充当部分view层的工作,难以完全解耦,并且随着项目的提上Controller会更加的臃肿,这样子的话模型图就会变成这个样子。
Android日记之MVC、MVP和MVVM
文章图片
模型图,来源见参考,侵删


MVP的使用 MVP就是Model-View-Presenter,在MVP中,是可以进行解耦分离的,因为在MVP中Model和View是不能直接通信的,它们只能通过Presenter来进行通信,这样子的话,Activity的功能就会被大幅度简化,不再充当控制器,主要做view层的工作就好了。

Android日记之MVC、MVP和MVVM
文章图片
MVP模型图,来源见参考,侵删
接着我们开始写代码,首先我们这里View层的那些展示功能主要以接口的方式进行实现,Model层主要提供查询数据方面的功能,Presenter层主要负责业务逻辑的处理。

Android日记之MVC、MVP和MVVM
文章图片
MVP各层功能,来源见参考,侵删
首先我们编写View层的接口。
public interface IMVPView {//获取用户输入的信息 String getUser(); //展示获取信息成功界面 void showSuccessPage(Account account); //示获取信息失败界面 void showErrorPage(); }

然后我们编写Presenter层和Model层的业务逻辑,Model的业务逻辑跟MVC是一样的。
//Model层的业务逻辑 public class MVPModel {//模拟请求数据 public void getAccountData(String accountName, ICallback callback){ Random random = new Random(); boolean isSuccess = random.nextBoolean(); if (isSuccess){ Account account = new Account(); account.setName(accountName); account.setLevel(100); callback.onSuccess(account); }else { callback.onFailed(); } } }//Presenter层 public class MVPPresenter{//持有View接口的引用 private IMVPView mView; //持有Model层的引用 private MVPModel mModel; //通过构造函数获得实现View层接口的Activity public MVPPresenter(IMVPView mView) { this.mView = mView; mModel = new MVPModel(); }//具体的业务逻辑 public void getData(String accountName) { mModel.getAccountData(accountName, new ICallback() { @Override public void onSuccess(Account account) { mView.showSuccessPage(account); }@Override public void onFailed() { mView.showErrorPage(); } }); } }

刚刚也说过了View层和Model进行交互就需要通过Presenter来进行交互,就可以让Presenter层获得View层和Model的引用来进行交互,然后在写具体的业务逻辑就好啦,最后我们在View层实现View层的接口,然后通过让View层持有Presenter的引用传递进去就行,最后通过getData()方法来获取最终的结果。
//View层的具体实现 public class MainActivity extends AppCompatActivity implements View.OnClickListener, IMVPView {private TextView textView; private EditText editText; private MVPPresenter mvpPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); //View层传递给Presenter mvpPresenter = new MVPPresenter(this); }private void initView() { editText = findViewById(R.id.edit_text); textView = findViewById(R.id.txt_text); findViewById(R.id.btn_text).setOnClickListener(this); }@Override public void onClick(View v) { mvpPresenter.getData(getUser()); }@Override public String getUser() { return editText.getText().toString(); }@Override public void showSuccessPage(Account account) { textView.setText("用户账号:"+ account.getName()+ "用户等级:"+ account.getLevel()); }@Override public void showErrorPage() { textView.setText("获取数据失败"); } }


Android日记之MVC、MVP和MVVM
文章图片
MVP结果
MVP的有点就很明显了,每个层的职责划分明显,更加易于维护,缺点也很明显,因为View主要是通过接口实现,如果项目复杂的话,接口是非常多的,而且Presenter的职责会越来越臃肿,使用MVP的建议就是接口一定要规范化,或者使用第三方插件来自动生成MVP代码。还有就是根据项目的复杂程度,部分简单的功能就没必要使用MVP模式来进行设计。


MVVM的使用 MVVM是全名Model-View-ViewModel的缩写,它跟MVP是很相似的,区别就在于Presenter替换成了ViewModel,它是在MVP的基础上实现了,数据视图的绑定(DataBinding),当数据变化时,视图也会自动更新,反之,当视图变化是,数据也会更新,优点就是减少了接口,也告别了繁琐的findViewById操作。

Android日记之MVC、MVP和MVVM
文章图片
MVVM模型,来源见参考,侵删
使用MVVM之前,我们就需要了解DataBinding的基本用法,DataBinding是谷歌推出的实现数据绑定的框架(数据与视图的双向绑定),它可以更好的帮助我们在Android中实现MVVM。要使用DataBinding,首先我们在Build文件下输入以下代码就可以使用了。
android { compileSdkVersion 28defaultConfig { applicationId "com.ju.mvvmdemo" minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } //输入这段代码 dataBinding { enabled = true }}

然后我们需要将布局修改为DataBinding布局。

然后我们就可以在data标签里面输入实体类了,我们把Account实体类输入进去。

接着我们在Activity里通过DataBindingUtil.setContentView()去绑定视图,这里注意以下,ActivityMainBinding是在build的时候自动生成的对应绑定视图,生成的名字就是你绑定的layout的名字。
public class MainActivity extends AppCompatActivity {@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); }}

然后我们创建Model层的逻辑,业务还是和之前MVP、MVC是一样的。
public class MVVMModel {//模拟请求数据 public void getAccountData(String accountName, ICallback callback){ Random random = new Random(); boolean isSuccess = random.nextBoolean(); if (isSuccess){ Account account = new Account(); account.setName(accountName); account.setLevel(100); callback.onSuccess(account); }else { callback.onFailed(); } } }

接下来就是重点了,怎么双向绑定数据和视图呢,其实我们在layout对应的控件输入格式@{******}就可以了,我们这样输入。

刚刚我们在data标签里面放入了viewModel,然后就可以在控件里面通过你想要的方法来实时更新view了,如果是view实时更新来改变数据的话(比如EditText),在@后面加一个 = 就好了。这里我们也设置了一个点击事件,当点击这个按钮后,会调用getData()方法,这里修改完毕后我们还需要Account实体类,我们让他继承BaseObservable,然后在你需要实时更新的变量设置相应的注解。
public class Account extends BaseObservable {private String name; private int level; public String getName() { return name; }public void setName(String name) { this.name = name; }//设置注解 @Bindable public int getLevel() { return level; }public void setLevel(int level) { this.level = level; //通过这段代码刷新视图 notifyPropertyChanged(BR.level); } }

然后就是重点了,接下来编写ViewModel层的代码。
public class MVVMViewModel extends BaseObservable {private MVVMModel mvvmModel; private String result; private String userInput; //一般传入Application对象,方便在ViewModel中使用Application //比如sharedpreferences需要使用 public MVVMViewModel(Application application) { mvvmModel = new MVVMModel(); }@Bindable public String getUserInput() { return userInput; }public void setUserInput(String userInput) { this.userInput = userInput; notifyPropertyChanged(BR.userInput); }@Bindable public String getResult() { return result; }public void setResult(String result) { this.result = result; notifyPropertyChanged(BR.result); }//点击按钮后调用这个方法,参数要View public void getData(View view) {mvvmModel.getAccountData(userInput, new ICallback() { @Override public void onSuccess(Account account) { String info = account.getName() + "|" + account.getLevel(); setResult(info); }@Override public void onFailed() { setResult("更新失败"); } }); } }

最后我们在Activity中和ViewModel绑定,就可以进行使用了。
public class MainActivity extends AppCompatActivity {@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); //实例化MVVMModel MVVMViewModel mvvmViewModel = new MVVMViewModel(getApplication()); //绑定ViewModel binding.setViewModel(mvvmViewModel); } }


Android日记之MVC、MVP和MVVM
文章图片
MVVM运行结果
MVVM优点也很明显,因为数据和视图进行了双向的绑定,这样就可以极大的简化了代码,缺点就是相较于MVP和MVC使用上会复杂很多,学习成本会很大。

【Android日记之MVC、MVP和MVVM】
参考
  • Android开发中的MVC_MVP_MVVM

    推荐阅读