Android开发中的MVC_MVP_MVVM
前言
MVC、MVP和MVVM是Android开发中常见的架构模式,这三种架构模式都有其独特的优点和适用场景。它们可以帮助开发者更好地组织和管理代码,提高应用程序的可维护性和可测试性,同时也能够提升开发效率和团队协作能力。选择适合的架构模式取决于项目的需求和开发团队的经验,理解和掌握这些架构模式对于Android开发者来说是非常重要的。现通过一个案例需求分析三种框架
案例需求
查询用户账号信息:用户输入账号,点击按钮可进行查询账号信息,如果查询数据成功,则将数据展示在界面上;如果查询数据失败,则在界面上提示获取数据失败
MVC框架模型
为什么学习MVC?
如果不使用任何框架来实现这个案例需求,则需要实现以下功能:
- 获取用户输入的信息
- 展示获取信息成功界面
- 展示获取信息失败界面
- 查询用户数据
- 业务逻辑
代码示例
Bean对象
//账号信息
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 MCallback {
void onSuccess(Account account);
void onFailed();
}
整体业务逻辑
public class NormalActivity extends AppCompatActivity implements View.OnClickListener {
private EditText mEtAccount;
private TextView mTvResult;
private Button mBtGetAccount;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
initView();
}
private void initView() {
mEtAccount = findViewById(R.id.et_account);
mTvResult = findViewById(R.id.tv_result);
mBtGetAccount = findViewById(R.id.btn_getAccount);
mBtGetAccount.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_getAccount:
String userInput = getUserInput();
getAccountData(userInput, new MCallback() {
@Override
public void onSuccess(Account account) {
showSuccessPage(account);
}
@Override
public void onFailed() {
showFailedPage();
}
});
}
}
//获取用户输入的信息
private String getUserInput() {
return mEtAccount.getText().toString();
}
//展示获取数据成功的界面
private void showSuccessPage(Account account) {
mTvResult.setText("用户账号:" + account.getName() + "|"
+ "用户等级:" + account.getLevel());
}
//展示获取数据失败的界面
private void showFailedPage() {
mTvResult.setText("获取数据失败");
}
//模拟查询账号数据
private void getAccountData(String accountName, MCallback 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();
}
}
}
MVC模型简介
MVC是一种经典的架构模式,将应用程序分为三个主要组成部分:模型(Model)、视图(View)和控制器(Controller)。模型负责数据管理和业务逻辑,视图负责用户界面展示,控制器处理用户输入和协调模型与视图之间的交互。
- Controller:Activity、Fragment
- View:layout、View控件
- Model:数据处理(网络请求、SQL等)
MVC代码演练
使用MVC框架实现案例需求的话,需要实现以下功能:
- MVCActivity(C层):业务逻辑处理、获取用户输入、展示成功页面、展示失败页面
- MVCModel层(M层):查询账号数据
- View层(V层):layout
- 将数据的获取与界面的展示分离(将查询账号数据从Acitity中分离到Model中即可)
- 解决各层之间通信问题(Activity通知Model获取数据,Model通知Activity更新界面)
将查询账号数据抽离到MVCModel中:
public class MVCModel {
//模拟查询账号数据
public void getAccountData(String accountName, MCallback 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();
}
}
}
MVC的优缺点
- 优点:
一定程度上实现了Model与View的分离,降低了代码的耦合度。 - 缺点:
Controller与View难以完全解耦,并且随着项目复杂度的提升,Controller将越来越臃肿。
MVP框架模型
MVP模型简介
MVP模式在MVC的基础上做了一些改进,将视图和模型之间的直接交互改为通过一个中间层——Presenter来完成。Presenter负责处理视图的逻辑和用户交互,并将数据获取和处理的任务委托给模型。
- Model与View不再直接进行通信,而是通过中间层Presenter来实现
- Activity的功能被简化,不再充当控制器,主要负责View层面的工作
MVP代码实战
使用MVP框架实现案例需求的话,需要实现以下功能:
- MVPActivity(V层):获取用户输入、展示成功界面、展示失败界面
- MVPPresenter(P层):业务逻辑处理
- MVPModel(M层):查询账号数据
- MVPctivity负责提供View层面的功能(采用实现接口的方式)
- MVPModel负责提供数据方面的功能
- Model与View不再进行直接通信,通过Presenter来实现
IMVPView接口设计
public interface IMVPView {
String getUserInput();
void showSuccessPage(Account account);
void showFailedPage();
}
MVPModel设计
public class MVPModel {
//模拟查询账号数据
public void getAccountData(String accountName, MCallback 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();
}
}
}
MVPPresenter设计
public class MVPPresenter {
private IMVPView imvpView;
private MVPModel mvpModel;
public MVPPresenter(IMVPView imvpView) {
this.imvpView = imvpView;
mvpModel = new MVPModel();
}
public void getData(String accountName) {
mvpModel.getAccountData(accountName, new MCallback() {
@Override
public void onSuccess(Account account) {
imvpView.showSuccessPage(account);
}
@Override
public void onFailed() {
imvpView.showFailedPage();
}
});
}
}
MVP的优缺点
- 优点:解决了MVC中Controller与View过度耦合的缺点,职责划分明显,更加易于维护
- 缺点:接口数量多,项目复杂度升高。随着项目复杂度的提升,Presenter层将越来越臃肿。
使用MVP的建议:
- 接口规范化(封装父类接口以减少接口的使用量)
- 使用第三方插件自动生成MVP代码
- 对于一些简单的界面,可以选择不使用框架
- 根据项目复杂度,部分模块可以选择不使用接口
MVVM框架模型
MVVM模型简介
MVVM模式进一步改进了MVP模式,引入了一个新的组件——ViewModel。ViewModel与视图进行双向绑定,负责处理视图的状态和逻辑,同时也能够监听模型的变化。这种双向绑定的机制使得视图与数据的同步更加方便,减少了手动更新视图的代码量。
- 减少了接口数量
- 告别繁琐findViewById操作
DataBinding学习
DataBinding是谷歌官方发布的一个实现数据绑定的框架(实现数据与视图的双向绑定),DataBinding可以帮助我们在安卓中更好的实现MVVM模式。
DataBinding使用步骤
- 启用DataBinding
- 修改布局文件为DataBinding布局
- 数据绑定
DataBinding实战
在 build.gradle(app)的android中启动DataBinding
dataBinding {
enabled = true
}
alt+enter或option+enter修改布局为DataBinding布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="account"
type="com.example.mdemo.bean.Account" />
<variable
name="activity"
type="com.example.mdemo.databinding.DemoActivity" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".databinding.DemoActivity">
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:gravity="center"
android:text="@{account.name+'|'+account.level}" />
<Button
android:id="@+id/btn_addLevel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:onClick="@{activity.onclick}"
android:text="账号等级+1" />
</LinearLayout>
</layout>
数据绑定
public class DemoActivity extends AppCompatActivity {
private Account account;
private ActivityDemoBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_demo);
account = new Account();
account.setName("TEST");
account.setLevel(100);
binding.setAccount(account);
binding.setActivity(this);
}
public void onclick(View view) {
Toast.makeText(this, "点击了", Toast.LENGTH_SHORT).show();
int level = account.getLevel();
account.setLevel(level + 1);
binding.setAccount(account);
}
}
代码优化
为减少binding.setAccount赋值实现数据更新的代码冗余操作,可借助BaseObservable接口、Bindable注解及notifyPropertyChanged实现数据自动更新操作
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);
}
}
MVVM代码实战
使用MVVM框架实现案例需求的话,需要实现以下功能:
- layout(V层):获取用户输入、展示成功界面、展示失败界面
- MVVMViewModel(VM层):业务逻辑处理、数据更新
- MVVMModel层(M层):查询账号数据
- 提供View、ViewModel以及Model三层
- 将布局修改为DataBinding布局
- View与ViewModel之间通过DataBinding进行通信
- 获取数据并展示在界面上
MVVMDataBinding布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.mdemo.mvvm.MVVMViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".mvvm.MVVMActivity">
<EditText
android:id="@+id/et_account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入要查询的账号"
android:layout_marginTop="30dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@={viewModel.userInput}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/btn_getAccount"
android:text="查询用户信息"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:onClick="@{viewModel.getData}"
app:layout_constraintTop_toTopOf="@id/et_account"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.result}"
app:layout_constraintTop_toTopOf="@+id/btn_getAccount"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="120dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MVVMViewModel设计
public class MVVMViewModel extends BaseObservable {
private MVVMModel mvvmModel;
private String userInput;
private ActivityMvvmactivityBinding binding;
private String result;
//一般需要传入Application对象,方便在ViewModel中使用application,
//比如sharedpreferences需要使用
public MVVMViewModel(Application application) {
mvvmModel = new MVVMModel();
}
public MVVMViewModel(Application application, ActivityMvvmactivityBinding binding) {
mvvmModel = new MVVMModel();
this.binding = binding;
}
@Bindable
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
notifyPropertyChanged(BR.result);
}
@Bindable
public String getUserInput() {
return userInput;
}
public void setUserInput(String userInput) {
this.userInput = userInput;
notifyPropertyChanged(BR.userInput);
}
public void getData(View view) {
// String userInput = binding.etAccount.getText().toString();
mvvmModel.getAccountData(userInput, new MCallback() {
@Override
public void onSuccess(Account account) {
String info = account.getName() + "|" + account.getLevel();
setResult(info);
}
@Override
public void onFailed() {
setResult("获取数据失败");
}
});
}
}
MVVMActivity
public class MVVMActivity extends AppCompatActivity {
private ActivityMvvmactivityBinding binding;
private MVVMViewModel mvvmViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_mvvmactivity);
mvvmViewModel = new MVVMViewModel(getApplication(),binding);
binding.setViewModel(mvvmViewModel);
}
}
LiveData+ViewModel
- LiveData是一个可以被观察的数据持有者,它可以通过添加观察者的方式来让其他组件观察它的变更
- LiveData遵从应用程序的生命周期(如果LiveData的观察者已经是销毁状态,LiveData就不会通知该观察者)
MVVM的优缺点
- 优点:实现了数据和视图的双向绑定,极大的简化代码
- 缺点:bug难以调试,并且dataBinding目前还存在一些编译问题
总结
- MVC:学习简单但是解耦不够彻底
- MVP:解耦更加彻底,学习相对简单,但是代码相对繁琐
- MVVM:代码逻辑简介,但是学习成本较大
链接:https://juejin.cn/post/7244050872127193146
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。