MVC
MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
组成部分
模型(Model):业务逻辑部分,具体的算法逻辑在这一部分中。
视图(View):UI组件,在Android中就是TextView、Button、Activity等组件。
控制器(Controller):View层收到的用户请求转发给控制器,控制器通知模型进行具体的操作。
请求流程
- 用户通过View层发出请求
- Controller接收请求
- Controller操作Model进行逻辑处理
- Model处理完后通知View更新数据(通常是通过回调)
- View现实更新的数据
举个例子
写个登录功能的例子吧,两个TextView、一个Button。
项目结构图
Model层
ILoginModel
// 一个接口,里面有一个方法,Model的实现类给出具体实现。
public interface ILoginModel {
void login(String account,String password);
}
ILoginModelImp
public class LoginModelImp implements ILoginModel { //实现ILoginModel接口,给出login方法的具体实现
private LoginCallBack mLoginCallBack;
public LoginModelImp(LoginCallBack mLoginCallBack) {
this.mLoginCallBack = mLoginCallBack;
}
@Override
public void login(String account, String password) {
if (TextUtils.isEmpty(account) || TextUtils.isEmpty(password)) {
if (mLoginCallBack != null)
mLoginCallBack.onFailed("账号或密码不能为空");
} else if (password.equals("12345678")) {
if (mLoginCallBack != null)
mLoginCallBack.onSucceed();
} else {
if (mLoginCallBack != null)
mLoginCallBack.onFailed("密码错误");
}
}
//回调接口,Model层处理完逻辑后回调,通知View更新数据。
public interface LoginCallBack {
void onSucceed();
void onFailed(String tips);
}
}
View层(Controller)
MVCActivity
public class MVCActivity extends AppCompatActivity implements View.OnClickListener {
private EditText mAccount, mPassword;
private Button mLogin;
// 持有一个Model的成员变量
private ILoginModel mLoginModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvc);
init();
mLogin.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.mvc_btn_login:
String accountText = mAccount.getText().toString();
String passwordText = mPassword.getText().toString();
// 调用Model层的login方法
mLoginModel.login(accountText, passwordText);
break;
}
}
private void init() {
mAccount = findViewById(R.id.mvc_ed_account);
mPassword = findViewById(R.id.mvc_ed_password);
mLogin = findViewById(R.id.mvc_btn_login);
// 初始化Model变量,并传入回调接口
mLoginModel = new LoginModelImp(new LoginModelImp.LoginCallBack() {
@Override
public void onSucceed() { // 成功则显示登录成功
Toast.makeText(MVCActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailed(String tips) { // 失败则显示错误信息
Toast.makeText(MVCActivity.this, tips, Toast.LENGTH_SHORT).show();
}
});
}
}
在这个例子中,用户点击登录按钮后,在onClik方法中会调用Model层的login方法,Model在判断账号密码是否正确后,通过回调通知View层,View再显示出登录结果。
优点
1.在大型项目中使项目结构更加清晰
2.降低了View和Model的耦合性
缺点
1.在Android中Controller和View不能彻底分离,Controller和View都在Activity中,当业务需求不断增长,Activity的代码会变得庞大臃肿。
2.Controller和View不能彻底分离,意味着View层和Model层还存在比较高的耦合度。
MVP
MVP和MVC最大的区别就是,在MVP中,View和Model彻底解耦,View和Model不再通信,而是通过Presenter(相当于桥梁)。
请求流程
1.用户通过View发出请求
2.Presenter接收用户请求
3.Presenter通知Model进行逻辑处理
4.Model处理完成后通过回调告诉Presenter
5.Presenter通知View更新数据
6.View显示更新的数据
还是那个例子
项目结构
Model层跟MVC中的一样
Presenter层
IPresenter
public interface IPresenter {
void login(String account,String password);
}
Presenter
public class Presenter implements IPresenter {
// 同时持有View、Model的成员变量,因为View与Model的通信是通过Presenter来进行的
private IView mView;
private ILoginModel mLoginModel;
public Presenter(IView view) {
this.mView = view;
mLoginModel = new LoginModelImp(new LoginModelImp.LoginCallBack() {
// 回调中通知View更新UI
@Override
public void onSucceed() {
mView.showText("登录成功");
}
@Override
public void onFailed(String tips) {
mView.showText(tips);
}
});
}
@Override
public void login(String account, String password) {
// 调用Model的login
mLoginModel.login(account, password);
}
}
View层
IView
// 通过一个接口定义View层所有更新UI的操作
public interface IView {
void showText(String text);
}
MVPActivity
public class MVPActivity extends AppCompatActivity implements IView, View.OnClickListener {
private EditText mAccount, mPassword;
private Button mLogin;
// 持有Presenter的成员变量,把用户请求转发到此presenter中
private IPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
init();
mLogin.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.mvp_btn_login:
String accountText = mAccount.getText().toString();
String passwordText = mPassword.getText().toString();
// 转发用户请求到presenter
mPresenter.login(accountText, passwordText);
break;
default:
break;
}
}
@Override
public void showText(String text) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
private void init() {
mAccount = findViewById(R.id.mvp_ed_account);
mPassword = findViewById(R.id.mvp_ed_password);
mLogin = findViewById(R.id.mvp_btn_login);
mPresenter = new Presenter(this);
}
}
优点
1)复杂的逻辑处理放在presenter进行处理,减少了activity的臃肿。
2)M层与V层完全分离,修改V层不会影响M层,降低了耦合性。
3)P层与V层的交互是通过接口来进行的,便于单元测试。
缺点
1)每个view都有presenter ,类相对比较多。
MVVM
Model-View-ViewModel
MVP的升级版,流程与MVP类似,但引入了Data Binding技术,实现View和ViewModel数据的双向绑定,进一步降低View和ViewModel的耦合度,减轻Activity的压力。
继续那个例子
DataBinding的用法大家可以自行上网查阅学习(笔者也是第一次用,不好介绍),这里把重点放在MVVM这个模式上。
项目结构图
Model层还是和前面的一样
ViewModel
这里ViewModel因为要和View绑定数据,所以有mAccount、mPassword、mOnClickListener,这些变量都跟View层的组件绑定在一起。除了这些外,剩下的代码和MVP中的Presenter类似。
LoginViewModel
// BaseObservable是databinding中的,当数据变更时,可以使用notify等方法通知View(这里例子没有用到)
public class LoginViewModel extends BaseObservable {
private IView mView;
private ILoginModel mLoginModel;
private String mAccount;
private String mPassword;
private View.OnClickListener mOnClickListener;
public String getAccount() {
return mAccount;
}
public String getPassword() {
return mPassword;
}
public void setAccount(String mAccount) {
this.mAccount = mAccount;
}
public void setPassword(String mPassword) {
this.mPassword = mPassword;
}
public View.OnClickListener getOnClickListener() {
return mOnClickListener;
}
public LoginViewModel(IView view, String mAccount, String mPassword) {
this.mView = view;
mLoginModel = new LoginModelImp(new LoginModelImp.LoginCallBack() {
@Override
public void onSucceed(String tips) {
mView.showText(tips);
}
@Override
public void onFailed(String tips) {
mView.showText(tips);
}
});
mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
};
this.mAccount = mAccount;
this.mPassword = mPassword;
}
public void login() {
mLoginModel.login(mAccount, mPassword);
}
}
View层
使用databinding的xml文件和普通的不一样,先看一下。
activity_mvvm.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewmodel"
type="com.example.mvdemo.mvvm.viewmodel.LoginViewModel" />
<variable
name="presenter"
type="com.example.mvdemo.mvvm.view.MVVMActivity.Presenter" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onTextChanged="@{presenter.onTextChanged1}"
android:text="@{viewmodel.account}" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onTextChanged="@{presenter.onTextChanged2}"
android:text="@{viewmodel.password}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{presenter.onClick}"
android:text="@string/btn_login_text" />
</LinearLayout>
</layout>
IView跟MVP的一样
MVVMActivity
package com.example.mvdemo.mvvm.view;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.example.mvdemo.R;
import com.example.mvdemo.databinding.ActivityMvvmBinding;
import com.example.mvdemo.mvvm.viewmodel.LoginViewModel;
public class MVVMActivity extends AppCompatActivity implements IView {
private ActivityMvvmBinding mBinding;
private LoginViewModel mLoginViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
mLoginViewModel = new LoginViewModel(this, "", "");
mBinding.setViewmodel(mLoginViewModel);
mBinding.setPresenter(new Presenter());
}
public class Presenter {
public void onTextChanged1(CharSequence s, int start, int before, int count) {
mLoginViewModel.setAccount(s.toString());
}
public void onTextChanged2(CharSequence s, int start, int before, int count) {
mLoginViewModel.setPassword(s.toString());
}
public void onClick(View view) {
mLoginViewModel.login();
}
}
@Override
public void showText(String text) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
}
注意这里的Presenter跟MVP中的不是同一个东西,这里的Presenter只是一个内部类,是提供给组件绑定事件用的;而MVP中的Presenter是一个层面。可以看到使用databinding技术的Activity非常简洁,没有了那些findViewById等代码,其他的跟MVP模式类似。而像ActivityMvvmBinding这些类是系统自动生成的,并不是自己创建的。
优点
1)引入Data Binding技术后,Activity减少了很多冗余的代码,如findViewById等,使得Activity的代码非常简洁。
2)双向绑定时,当Model变化时,View-Model会自动更新,View也会自动变化。
缺点
1)数据绑定使得 Bug 很难被调试。
2)数据双向绑定不利于代码重用。
总结
其实MV+X模式的目的都是解耦,把View和Model分离,View层只负责更新UI,Model层负责具体的算法逻辑、耗时操作等。值得注意的是,不能为了用模式而使用模式,模式并不能简化我们的系统,甚至可能使系统更加难以理解(多了很多的接口),但在大型项目,这些代价换来的是良好的灵活性和易维护性。