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

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYOS26qW' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片