rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • MVPVM

MVPVM

可以看到MVPVM 其实就是MVP的变种,加入了MVVM事件特性,增加了ViewModel,功能分类: View:只做视图更新操作 Model: 只做数据处理,网络数据 、本地数据 Presenter: 只做业务逻辑处理,View或者Model 事件分发 ViewModel: 绑定View 和 Model,添加数据变更监视器

从0开始搭建MVP+ViewModel框架的android应用01---MVPVM诞生记

在 Android 架构中,MVPVM 并不是一个官方或广泛认可的标准模式,而是开发者开发者在实践中结合 MVP(Model-View-Presenter) 和 MVVM(Model-View-ViewModel) 思想形成的混合架构。其核心是保留 MVP 中 Presenter 的业务协调能力,同时引入 MVVM 中 ViewModel 的数据持有与生命周期感知特性,解决 MVP 中状态管理复杂和内存泄漏的问题。

MVPVM 核心组件与设计思想

MVPVM 在 MVP 基础上增加了 ViewModel 层,明确各组件职责:

组件职责与其他模式的区别
Model处理数据逻辑(网络请求、数据库操作等),不依赖其他层。与 MVP、MVVM 一致,纯数据层。
View展示数据、接收用户交互,通过接口与 Presenter 通信,不包含业务逻辑。同 MVP 的 View,但不再直接依赖 Presenter 更新 UI,而是观察 ViewModel 的数据变化。
Presenter协调业务逻辑:接收 View 的交互指令 → 调用 Model 处理数据 → 更新 ViewModel。比 MVP 的 Presenter 更 “轻量”,不直接操作 View,而是通过 ViewModel 传递数据。
ViewModel持有 UI 状态数据(如加载状态、表单数据),具有生命周期感知(与 Activity 生命周期解耦),通过 LiveData 通知 View 更新。引入 MVVM 的 ViewModel 特性,解决 MVP 中状态恢复和内存泄漏问题。

核心交互流程

用户交互 → View → Presenter → Model(处理数据)
                                          ↓
ViewModel(更新数据)← Presenter ← 处理结果
    ↓
View(观察数据变化,更新UI)

Java 代码示例(登录功能)

以登录功能为例,展示 MVPVM 各组件的协作:

1. Model 层(数据与业务逻辑)

与 MVP 一致,负责数据处理:

// 数据模型
public class User {
    private String username;
    private String password;
    // 构造函数、getter
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public String getUsername() { return username; }
    public String getPassword() { return password; }
}

// 登录业务逻辑
public class LoginModel {
    public void login(User user, LoginCallback callback) {
        new Thread(() -> { // 模拟网络请求
            try {
                Thread.sleep(1000);
                if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
                    callback.onSuccess("登录成功");
                } else {
                    callback.onFailure("用户名或密码错误");
                }
            } catch (InterruptedException e) {
                callback.onFailure("网络异常");
            }
        }).start();
    }

    public interface LoginCallback {
        void onSuccess(String message);
        void onFailure(String error);
    }
}

2. ViewModel 层(持有状态数据)

使用 LiveData 存储 UI 状态,具有生命周期感知:

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class LoginViewModel extends ViewModel {
    // 私有可变数据(仅内部更新)
    private final MutableLiveData<Boolean> _isLoading = new MutableLiveData<>(false);
    private final MutableLiveData<String> _resultMessage = new MutableLiveData<>("");
    private final MutableLiveData<String> _username = new MutableLiveData<>("");
    private final MutableLiveData<String> _password = new MutableLiveData<>("");

    // 暴露不可变的 LiveData 给 View 观察
    public LiveData<Boolean> isLoading = _isLoading;
    public LiveData<String> resultMessage = _resultMessage;
    public LiveData<String> username = _username;
    public LiveData<String> password = _password;

    // 更新状态的方法(供 Presenter 调用)
    public void setLoading(boolean loading) {
        _isLoading.setValue(loading);
    }

    public void setResultMessage(String message) {
        _resultMessage.setValue(message);
    }

    public void setUsername(String username) {
        _username.setValue(username);
    }

    public void setPassword(String password) {
        _password.setValue(password);
    }
}

3. Presenter 层(协调逻辑)

接收 View 指令,调用 Model 处理数据,更新 ViewModel:

public class LoginPresenter {
    private LoginModel model;
    private LoginViewModel viewModel;

    // 构造函数:注入 Model 和 ViewModel
    public LoginPresenter(LoginModel model, LoginViewModel viewModel) {
        this.model = model;
        this.viewModel = viewModel;
    }

    // 处理登录逻辑
    public void login(String username, String password) {
        // 1. 更新 ViewModel:显示加载
        viewModel.setLoading(true);
        viewModel.setResultMessage("");

        // 2. 调用 Model 登录
        User user = new User(username, password);
        model.login(user, new LoginModel.LoginCallback() {
            @Override
            public void onSuccess(String message) {
                // 更新 ViewModel:登录成功
                viewModel.setLoading(false);
                viewModel.setResultMessage(message);
            }

            @Override
            public void onFailure(String error) {
                // 更新 ViewModel:登录失败
                viewModel.setLoading(false);
                viewModel.setResultMessage(error);
            }
        });
    }

    // 处理用户名输入变化
    public void onUsernameChanged(String username) {
        viewModel.setUsername(username);
    }

    // 处理密码输入变化
    public void onPasswordChanged(String password) {
        viewModel.setPassword(password);
    }
}

4. View 层(Activity)

观察 ViewModel 数据变化更新 UI,用户交互通过 Presenter 处理:

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.text.Editable;
import android.text.TextWatcher;

public class LoginActivity extends AppCompatActivity {
    private EditText etUsername;
    private EditText etPassword;
    private Button btnLogin;
    private ProgressBar progressBar;
    private TextView tvResult;
    private LoginPresenter presenter;
    private LoginViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        initView();

        // 初始化 ViewModel(生命周期感知)
        viewModel = new ViewModelProvider(this).get(LoginViewModel.class);
        // 初始化 Presenter(注入 Model 和 ViewModel)
        presenter = new LoginPresenter(new LoginModel(), viewModel);

        // 观察 ViewModel 数据变化,更新 UI
        observeViewModel();

        // 绑定用户交互到 Presenter
        bindEvents();
    }

    private void initView() {
        etUsername = findViewById(R.id.et_username);
        etPassword = findViewById(R.id.et_password);
        btnLogin = findViewById(R.id.btn_login);
        progressBar = findViewById(R.id.progress_bar);
        tvResult = findViewById(R.id.tv_result);
    }

    private void observeViewModel() {
        // 观察加载状态
        viewModel.isLoading.observe(this, isLoading -> {
            progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
        });

        // 观察结果信息
        viewModel.resultMessage.observe(this, message -> {
            tvResult.setText(message);
        });

        // 观察用户名(可选,用于回显)
        viewModel.username.observe(this, username -> {
            etUsername.setText(username);
        });
    }

    private void bindEvents() {
        // 用户名输入变化 → 通知 Presenter
        etUsername.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable s) {
                presenter.onUsernameChanged(s.toString());
            }
            // 其他方法省略
        });

        // 密码输入变化 → 通知 Presenter
        etPassword.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable s) {
                presenter.onPasswordChanged(s.toString());
            }
            // 其他方法省略
        });

        // 登录按钮点击 → 通知 Presenter
        btnLogin.setOnClickListener(v -> {
            String username = etUsername.getText().toString();
            String password = etPassword.getText().toString();
            presenter.login(username, password);
        });
    }
}

MVPVM 的应用场景

  1. 需要状态持久化的场景:如屏幕旋转、进程重建时需保留用户输入或加载状态(依赖 ViewModel 的生命周期特性)。
  2. 中等复杂度项目:既需要 Presenter 清晰的业务协调能力,又希望简化状态管理(避免 MVP 中手动恢复状态的繁琐)。
  3. 团队混合技能栈:团队中部分成员熟悉 MVP,部分熟悉 MVVM,MVPVM 可作为过渡方案。

优点

  1. 状态管理简化:ViewModel 持有 UI 状态并与 Activity 生命周期解耦,解决了 MVP 中屏幕旋转后状态丢失的问题。
  2. 内存泄漏风险降低:Presenter 不再直接持有 View 引用,而是通过 ViewModel 传递数据,减少因 View 销毁导致的泄漏。
  3. 业务逻辑与 UI 解耦更彻底:View 仅通过观察 ViewModel 更新 UI,Presenter 专注业务逻辑,职责更单一。
  4. 可测试性保留:Presenter 和 ViewModel 均不依赖 Android 框架,可通过 JUnit 独立测试。

缺点

  1. 层级冗余:相比 MVP 多了 ViewModel 层,简单场景下代码量增加(需维护 Presenter 和 ViewModel 两个中间层)。
  2. 学习成本高:需要同时理解 MVP 的 Presenter 协调逻辑和 MVVM 的 ViewModel/LiveData 数据驱动思想。
  3. 职责边界模糊:若设计不当,可能出现 Presenter 和 ViewModel 职责重叠(如两者都处理部分业务逻辑),反而增加复杂度。
  4. 不如纯 MVVM 简洁:对于复杂交互,MVVM 可通过 DataBinding 进一步简化 View 与 ViewModel 的绑定,而 MVPVM 仍需 Presenter 中转,略显繁琐。

总结

MVPVM 是 MVP 向 MVVM 过渡的混合架构,通过引入 ViewModel 解决了 MVP 的状态管理和内存泄漏问题,同时保留了 Presenter 对业务逻辑的清晰控制。其优点是状态持久化能力强、解耦更彻底,缺点是层级冗余、学习成本高。实际开发中,若团队已熟悉 MVVM,更推荐直接使用纯 MVVM(结合 DataBinding/Compose);若需兼容现有 MVP 代码,MVPVM 可作为折中方案。

最近更新:: 2025/10/28 00:02
Contributors: luokaiwen