文章目录
一、MVVM 架构概述
1. 相关概念说明
定义: MVVM(Model-View-ViewModel)是一种软件架构模式,已成为现代UI应用程序开发的主流模式之一。它通过将UI逻辑与业务逻辑分离,简化了开发过程,提高了代码的可维护性和可测试性。

它将应用程序分为三个核心组件:
- Model(模型):数据模型层,负责业务逻辑和数据访问,与UI完全无关。
- View(视图):负责用户界面显示,但视图是被动的,它通过数据绑定从ViewModel获取数据并显示。
- ViewModel(视图模型):连接View和Model的桥梁,负责处理视图逻辑和状态管理
MVVM的核心思想:通过数据绑定和命令实现View和ViewModel的松耦合。这种方式降低了直接操作UI元素的需要,使代码更易于维护和测试。
二、MVVM 核心概念
1. 数据绑定
数据绑定是MVVM模式的核心机制,它建立了View与ViewModel之间的自动同步关系。当ViewModel中的数据变化时,View会自动更新;同样,当用户在View中输入数据时,这些变化也会自动反映到ViewModel中。
数据绑定的实现主要依赖于以下几个关键技术:
- 数据劫持/代理:通过Object.defineProperty或Proxy等技术拦截对象属性的访问和修改。
- 发布-订阅模式:建立数据变化与UI更新之间的通知机制。
- 数据监听:观察数据变化并触发相应的更新操作。
以下是简化的数据绑定实现示例:
① 基础 Observable 类
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Observable {
private PropertyChangeSupport support = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
protected void notifyPropertyChange(String propertyName, Object oldValue, Object newValue) {
support.firePropertyChange(propertyName, oldValue, newValue);
}
}
② ViewModel 基类
public abstract class ViewModel extends Observable {
// 通用的 ViewModel 逻辑可以放在这里
}
③ 具体 ViewModel 实现
public class UserViewModel extends ViewModel {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
String oldValue = this.name;
this.name = name;
notifyPropertyChange("name", oldValue, name);
}
public int getAge() {
return age;
}
public void setAge(int age) {
int oldValue = this.age;
this.age = age;
notifyPropertyChange("age", oldValue, age);
}
}
④ 数据绑定工具类
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class DataBinder {
public static void bind(View view, ViewModel viewModel) {
// 这里简化实现,实际应用中可能需要更复杂的绑定逻辑
if (view instanceof TextView && viewModel instanceof UserViewModel) {
TextView textView = (TextView) view;
UserViewModel userViewModel = (UserViewModel) viewModel;
// 初始值设置
textView.setText(userViewModel.getName());
// 监听ViewModel变化更新View
userViewModel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("name".equals(evt.getPropertyName())) {
textView.setText((String) evt.getNewValue());
}
}
});
// 监听View变化更新ViewModel (简化版,实际需要处理用户输入事件)
textView.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(String newText) {
userViewModel.setName(newText);
}
});
}
}
}
⑤ 简化的 View 组件 (模拟 Android TextView)
public class TextView {
private String text;
private TextWatcher textWatcher;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
System.out.println("TextView updated: " + text);
}
public void addTextChangedListener(TextWatcher textWatcher) {
this.textWatcher = textWatcher;
}
// 模拟用户输入
public void simulateUserInput(String newText) {
this.text = newText;
if (textWatcher != null) {
textWatcher.onTextChanged(newText);
}
}
public interface TextWatcher {
void onTextChanged(String newText);
}
}
⑥ 使用示例
public class MVVMDemo {
public static void main(String[] args) {
// 创建ViewModel
UserViewModel userViewModel = new UserViewModel();
userViewModel.setName("John Doe");
userViewModel.setAge(30);
// 创建View
TextView textView = new TextView();
// 绑定View和ViewModel
DataBinder.bind(textView, userViewModel);
// 测试ViewModel变化更新View
userViewModel.setName("Jane Smith"); // 控制台会输出TextView更新
// 测试View变化更新ViewModel
textView.simulateUserInput("New Name"); // ViewModel的name会被更新
System.out.println("ViewModel name: " + userViewModel.getName());
}
}
说明
这个简化示例展示了MVVM架构的核心数据绑定概念:
- ViewModel 继承自 Observable,当数据变化时通知观察者
- View (TextView) 提供UI展示和用户交互
- DataBinder 负责将View和ViewModel双向绑定
- ViewModel → View: 通过属性变化监听
- View → ViewModel: 通过用户输入事件监听
实际框架(如Android的Data Binding库)会使用更复杂的实现,包括注解处理、表达式解析等,但这个示例展示了基本原理。
- 车载应用常用框架:Android的Data Binding、Jetpack Compose,Qt的QML绑定
2. 命令模式实现
命令是MVVM模式中处理用户交互的主要方式。命令将UI事件(如按钮点击)绑定到ViewModel中的方法上,实现了用户操作与业务逻辑的解耦。
典型的命令实现通常包括:
- 可执行状态管理(CanExecute)
- 执行操作(Execute)
- 可执行状态变更通知(CanExecuteChanged)
以下是简化的命令模式实现示例:
① 命令接口
public interface Command {
void execute();
}
② 基础 ViewModel 类 (带命令支持)
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.HashMap;
import java.util.Map;
public class ViewModel {
private PropertyChangeSupport support = new PropertyChangeSupport(this);
private Map<String, Command> commands = new HashMap<>();
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
protected void notifyPropertyChange(String propertyName, Object oldValue, Object newValue) {
support.firePropertyChange(propertyName, oldValue, newValue);
}
public void registerCommand(String name, Command command) {
commands.put(name, command);
}
public Command getCommand(String name) {
return commands.get(name);
}
public void executeCommand(String name) {
Command command = commands.get(name);
if (command != null) {
command.execute();
}
}
}
③ 具体 ViewModel 实现
public class LoginViewModel extends ViewModel {
private String username;
private String password;
private String status;
public LoginViewModel() {
// 注册登录命令
registerCommand("login", new LoginCommand());
}
// 属性 getter/setter
public String getUsername() {
return username;
}
public void setUsername(String username) {
String oldValue = this.username;
this.username = username;
notifyPropertyChange("username", oldValue, username);
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
String oldValue = this.password;
this.password = password;
notifyPropertyChange("password", oldValue, password);
}
public String getStatus() {
return status;
}
private void setStatus(String status) {
String oldValue = this.status;
this.status = status;
notifyPropertyChange("status", oldValue, status);
}
// 登录命令内部类
private class LoginCommand implements Command {
@Override
public void execute() {
// 模拟登录逻辑
if (username != null && !username.isEmpty() &&
password != null && !password.isEmpty()) {
setStatus("登录中...");
// 模拟异步操作
new Thread(() -> {
try {
Thread.sleep(1000); // 模拟网络请求
setStatus("登录成功!");
} catch (InterruptedException e) {
setStatus("登录失败: " + e.getMessage());
}
}).start();
} else {
setStatus("用户名和密码不能为空");
}
}
}
}
④ 简化的 View 组件
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class LoginView {
private LoginViewModel viewModel;
public LoginView(LoginViewModel viewModel) {
this.viewModel = viewModel;
setupBindings();
}
private void setupBindings() {
// 监听ViewModel属性变化
viewModel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
Object newValue = evt.getNewValue();
if ("status".equals(propertyName)) {
System.out.println("状态更新: " + newValue);
}
}
});
}
// 模拟用户输入
public void setUsername(String username) {
viewModel.setUsername(username);
}
public void setPassword(String password) {
viewModel.setPassword(password);
}
// 模拟按钮点击
public void onLoginButtonClick() {
viewModel.executeCommand("login");
}
}
⑤ 使用示例
public class CommandPatternDemo {
public static void main(String[] args) {
// 创建ViewModel
LoginViewModel viewModel = new LoginViewModel();
// 创建View并绑定ViewModel
LoginView loginView = new LoginView(viewModel);
// 模拟用户操作
loginView.setUsername("admin");
loginView.setPassword("123456");
// 触发登录命令
loginView.onLoginButtonClick(); // 输出: 状态更新: 登录中... (1秒后) 状态更新: 登录成功!
// 测试空用户名密码
loginView.setUsername("");
loginView.setPassword("");
loginView.onLoginButtonClick(); // 输出: 状态更新: 用户名和密码不能为空
}
}
说明
- 命令接口 (Command):
- 定义了 execute() 方法,封装具体的操作逻辑
- ViewModel 增强:
- 添加了命令注册和执行功能
- 维护一个命令映射表 (commands)
- 提供 registerCommand(), getCommand() 和 executeCommand() 方法
- 具体命令实现:
- LoginCommand 作为内部类实现,可以访问 ViewModel 的所有成员
- 封装了登录业务逻辑,包括验证和异步操作
- View 交互:
- View 通过调用 executeCommand() 触发命令
- 命令执行结果通过属性变更通知 View 更新
- 优势:
- 将业务逻辑封装在命令中,与 UI 解耦
- 便于单元测试命令逻辑
- 支持命令的组合、撤销等扩展功能
3. 观察者模式
ViewModel通过观察者模式通知View更新,常见实现方式:
- LiveData (Android)
- Observable (RxJava, Kotlin Flow)
- Signals & Slots (Qt框架)
三、车载应用中的MVVM实现
1. 车载环境特点
- 硬件资源有限
- 高安全性和稳定性要求
- 多屏幕、多交互方式(触摸、旋钮、语音)
- 严格的性能要求
2. 推荐技术栈
- Android Automotive:ViewModel + LiveData + Data Binding
- Qt Automotive:QML + C++ ViewModel
- 跨平台方案:Flutter + Provider/Bloc
3. 车载特有考虑
采用 Data Binding 作为示例:
① ViewModel:负责管理UI相关的数据,生命周期感知,配置变化时数据不丢失。
// ViewModel
public class ClimateViewModel extends ViewModel {
private MutableLiveData<Integer> temperature = new MutableLiveData<>();
public ClimateViewModel() {
temperature.setValue(22); // 默认温度
}
public LiveData<Integer> getTemperature() {
return temperature;
}
public void increaseTemp() {
if (temperature.getValue() != null && temperature.getValue() < 30) {
temperature.setValue(temperature.getValue() + 1);
}
}
}
LiveData:生命周期感知的数据持有者,自动管理订阅避免内存泄漏。
Data Binding:在布局文件中直接绑定ViewModel数据,减少样板代码。
② Activity类
public class ClimateActivity extends AppCompatActivity {
private ActivityClimateBinding binding;
private ClimateViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Data Binding初始化
binding = DataBindingUtil.setContentView(this, R.layout.activity_climate);
// 获取ViewModel
viewModel = new ViewModelProvider(this).get(ClimateViewModel.class);
// 绑定ViewModel到布局
binding.setViewModel(viewModel);
binding.setLifecycleOwner(this);
// 传统方式观察LiveData(可选)
viewModel.getTemperature().observe(this, temp -> {
// 这里可以执行额外的UI更新逻辑
Log.d("Climate", "Temperature updated: " + temp);
});
}
}
③ 布局文件 (activity_climate.xml)
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.automotive.ClimateViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(viewModel.temperature)}"
android:textSize="24sp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Increase"
android:onClick="@{() -> viewModel.increaseTemp()}"/>
</LinearLayout>
</layout>
四、MVVM在车载开发中的最佳实践
1. 视图层设计原则
- 保持View尽可能简单
- 使用声明式UI(如Jetpack Compose/QML)
- 处理多种输入方式(触摸、旋钮、语音)
2. ViewModel设计要点
- 避免在ViewModel中持有View引用
- 使用状态容器管理复杂UI状态
- 考虑车载环境的生命周期管理
3. 模型层优化
- 实现硬件抽象层(HAL)隔离业务逻辑
- 使用Repository模式统一数据源
- 考虑车载网络不稳定的缓存策略
参考文章:https://blog.youkuaiyun.com/O_____V_____O/article/details/147440989
1475

被折叠的 条评论
为什么被折叠?



