注册

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)。模型负责数据管理和业务逻辑,视图负责用户界面展示,控制器处理用户输入和协调模型与视图之间的交互。


截屏2023-06-07 15.27.41.png



  • Controller:Activity、Fragment
  • View:layout、View控件
  • Model:数据处理(网络请求、SQL等)

MVC代码演练


使用MVC框架实现案例需求的话,需要实现以下功能:



  • MVCActivity(C层):业务逻辑处理、获取用户输入、展示成功页面、展示失败页面
  • MVCModel层(M层):查询账号数据
  • View层(V层):layout


  1. 将数据的获取与界面的展示分离(将查询账号数据从Acitity中分离到Model中即可)
  2. 解决各层之间通信问题(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负责处理视图的逻辑和用户交互,并将数据获取和处理的任务委托给模型。
截屏2023-06-12 16.34.57.png



  1. Model与View不再直接进行通信,而是通过中间层Presenter来实现
  2. Activity的功能被简化,不再充当控制器,主要负责View层面的工作

MVP代码实战


使用MVP框架实现案例需求的话,需要实现以下功能:



  • MVPActivity(V层):获取用户输入、展示成功界面、展示失败界面
  • MVPPresenter(P层):业务逻辑处理
  • MVPModel(M层):查询账号数据


  1. MVPctivity负责提供View层面的功能(采用实现接口的方式)
  2. MVPModel负责提供数据方面的功能
  3. 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的建议:



  1. 接口规范化(封装父类接口以减少接口的使用量)
  2. 使用第三方插件自动生成MVP代码
  3. 对于一些简单的界面,可以选择不使用框架
  4. 根据项目复杂度,部分模块可以选择不使用接口

MVVM框架模型


MVVM模型简介


MVVM模式进一步改进了MVP模式,引入了一个新的组件——ViewModel。ViewModel与视图进行双向绑定,负责处理视图的状态和逻辑,同时也能够监听模型的变化。这种双向绑定的机制使得视图与数据的同步更加方便,减少了手动更新视图的代码量。


截屏2023-06-13 11.27.10.png



  1. 减少了接口数量
  2. 告别繁琐findViewById操作

DataBinding学习


DataBinding是谷歌官方发布的一个实现数据绑定的框架(实现数据与视图的双向绑定),DataBinding可以帮助我们在安卓中更好的实现MVVM模式。


DataBinding使用步骤



  1. 启用DataBinding
  2. 修改布局文件为DataBinding布局
  3. 数据绑定

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层):查询账号数据


  1. 提供View、ViewModel以及Model三层
  2. 将布局修改为DataBinding布局
  3. View与ViewModel之间通过DataBinding进行通信
  4. 获取数据并展示在界面上

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:代码逻辑简介,但是学习成本较大

作者:Coward881
链接:https://juejin.cn/post/7244050872127193146
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册