MVVM(Model-View-ViewModel)是一种软件架构模式,广泛应用于 Android 应用开发中,用于分离关注点并提高代码的可维护性和可测试性。以下是 MVVM 在 Android 中的具体含义和组成部分:

1. Model(模型层)

职责:负责数据的获取、存储和管理。

实现方式:

数据可以来自本地数据库(如 Room)、网络请求(如 Retrofit)或其他数据源。

提供数据给 ViewModel 层。

示例:

MVVM(Model-View-ViewModel)是一种软件架构模式,广泛应用于 Android 应用开发中,用于分离关注点并提高代码的可维护性和可测试性。以下是 MVVM 在 Android 中的具体含义和组成部分:
 
1. Model(模型层)
职责:负责数据的获取、存储和管理。
实现方式:
数据可以来自本地数据库(如 Room)、网络请求(如 Retrofit)或其他数据源。
提供数据给 ViewModel 层。
示例:

2. View(视图层)

职责:负责 UI 的展示和用户交互。

实现方式:

使用 XML 文件定义布局。

通过绑定机制(Data Binding 或 View Binding)与 ViewModel 进行交互。

特点:

不直接与 Model 层交互。

只负责显示数据和响应用户操作。

3. ViewModel(视图模型层)

职责:

充当 Model 和 View 之间的桥梁。

负责准备和管理 View 所需的数据。

处理用户的输入并更新 Model。

特点:

生命周期感知(Lifecycle-Aware),不会因为配置更改(如屏幕旋转)而丢失数据。

使用 LiveData 或 StateFlow 等观察型数据结构,通知 View 数据变化。

示例:

  public class UserViewModel extends ViewModel {
      private MutableLiveData<UserModel> userLiveData = new MutableLiveData<>();

      public LiveData<UserModel> getUser() {
          return userLiveData;
      }

      public void loadUser() {
          // 模拟从 Model 层加载数据
          UserModel user = new UserModel();
          user.setName("Alice");
          user.setEmail("alice@example.com");
          userLiveData.setValue(user);
      }
  }
  

4. Android 中的实现工具

Data Binding / View Binding:

用于将 View 和 ViewModel 绑定在一起,减少手动操作控件的代码。

LiveData / StateFlow:

观察型数据容器,当数据发生变化时自动通知 View 更新。

Lifecycle:

提供生命周期感知功能,确保 ViewModel 在 Activity/Fragment 生命周期内有效。

5. MVVM 的优点

分离关注点:UI 层和业务逻辑层分离,便于维护和扩展。

可测试性:ViewModel 不依赖于 View,可以单独进行单元测试。

生命周期管理:ViewModel 自动处理 Activity/Fragment 的生命周期问题,避免内存泄漏。

6. 简单示例

以下是一个完整的 MVVM 示例:

(1) Model 层

public class UserModel {
    private String name;
    private String email;

    public UserModel(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() { return name; }
    public String getEmail() { return email; }
}

(2) ViewModel 层

public class UserViewModel extends ViewModel {
    private MutableLiveData<UserModel> userLiveData = new MutableLiveData<>();

    public LiveData<UserModel> getUser() {
        return userLiveData;
    }

    public void loadUser() {
        UserModel user = new UserModel("Alice", "alice@example.com");
        userLiveData.setValue(user);
    }
}

(3) View 层(XML + Data Binding)

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.example.UserViewModel" />
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:text="@{viewModel.user.name}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:text="@{viewModel.user.email}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</layout>

当然,以下是每一行代码的详细解释:

xml

深色版本

<layout xmlns:android="http://schemas.android.com/apk/res/android">
  • 定义一个根元素 <layout>,这是 Data Binding 布局文件的根标签。

  • xmlns:android="http://schemas.android.com/apk/res/android":声明 Android 命名空间,以便在布局文件中使用 Android 属性。

xml

深色版本

    <data>
  • 开始定义数据绑定相关的变量和导入的类。

  • 这个块内的内容用于声明可以在布局文件中使用的变量和表达式。

xml

深色版本

        <variable
            name="viewModel"
            type="com.example.UserViewModel" />
  • 声明一个名为 viewModel 的变量,其类型为 com.example.UserViewModel

  • 这个变量可以在布局文件中使用,用于访问 UserViewModel 中的数据。

xml

深色版本

    </data>
  • 结束 <data> 标签,表示数据绑定变量声明结束。

xml

深色版本

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
  • 定义一个 LinearLayout 布局容器。

  • android:orientation="vertical":设置布局的方向为垂直排列。

  • android:layout_width="match_parent":设置布局的宽度为父视图的宽度。

  • android:layout_height="match_parent":设置布局的高度为父视图的高度。

xml

深色版本

        <TextView
            android:text="@{viewModel.user.name}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
  • 定义一个 TextView 控件。

  • android:text="@{viewModel.user.name}":使用 Data Binding 表达式将 TextView 的文本内容绑定到 viewModel 对象的 user 属性的 name 字段。

  • android:layout_width="wrap_content":设置 TextView 的宽度根据内容自动调整。

  • android:layout_height="wrap_content":设置 TextView 的高度根据内容自动调整。

xml

深色版本

        <TextView
            android:text="@{viewModel.user.email}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
  • 定义另一个 TextView 控件。

  • android:text="@{viewModel.user.email}":使用 Data Binding 表达式将 TextView 的文本内容绑定到 viewModel 对象的 user 属性的 email 字段。

  • android:layout_width="wrap_content":设置 TextView 的宽度根据内容自动调整。

  • android:layout_height="wrap_content":设置 TextView 的高度根据内容自动调整。

xml

深色版本

    </LinearLayout>
  • 结束 LinearLayout 标签,表示线性布局容器结束。

xml

深色版本

</layout>
  • 结束 <layout> 标签,表示整个 Data Binding 布局文件结束。

总结

这个 XML 文件是一个 Data Binding 布局文件,用于在 Android 应用程序中绑定数据。它包含一个 LinearLayout 布局容器,其中有两个 TextView 控件,分别显示从 UserViewModel 获取的用户姓名和电子邮件地址。通过 Data Binding 表达式,这些 TextView 可以动态地更新显示的内容。

(4) Activity 或 Fragment

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        UserViewModel viewModel = new ViewModelProvider(this).get(UserViewModel.class);
        viewModel.loadUser();

        setContentView(R.layout.activity_main);
        ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
        binding.setViewModel(viewModel);
        binding.executePendingBindings();
    }
}

MutableLiveData<UserModel>与LiveData<UserModel>有区别吗?

是的,MutableLiveData<UserModel>LiveData<UserModel> 之间有一些关键的区别。下面是详细的解释:

LiveData<UserModel>

  • 不可变性: LiveData 是一个只读的数据持有者类。这意味着其他组件(如 Activity 或 Fragment)可以通过 LiveData 观察数据的变化,但不能直接修改这些数据。

  • 用途: 主要用于向观察者提供数据更新的通知,而不允许外部直接更改数据。

  • 方法:

    • getValue(): 获取当前存储的值。

    • observe(LifecycleOwner owner, Observer<T> observer): 添加一个观察者,当数据变化时会通知该观察者。

    • removeObserver(Observer<T> observer): 移除指定的观察者。

MutableLiveData<UserModel>

  • 可变性: MutableLiveData 继承自 LiveData,但它提供了额外的方法来修改存储的数据。

  • 用途: 主要用于在 ViewModel 中修改和更新数据,并通过 LiveData 将这些变化通知给观察者。

  • 方法:

    • 所有 LiveData 的方法。

    • setValue(T value): 设置一个新的值,并通知所有活跃的观察者。

    • postValue(T value): 在主线程上设置一个新的值,并通知所有活跃的观察者。这个方法适用于从后台线程发布更新。

示例代码对比

LiveData<UserModel>

java

深色版本

public class UserViewModel extends ViewModel {
    private final LiveData<UserModel> userLiveData;

    public UserViewModel(UserModel userModel) {
        this.userLiveData = new MutableLiveData<>(userModel);
    }

    public LiveData<UserModel> getUser() {
        return userLiveData;
    }
}

在这个示例中,UserViewModel 接受一个 UserModel 对象并通过构造函数初始化 LiveData。由于 LiveData 是不可变的,外部无法直接修改 userLiveData 的值。

MutableLiveData<UserModel>

java

深色版本

public class UserViewModel extends ViewModel {
    private final MutableLiveData<UserModel> userLiveData = new MutableLiveData<>();

    public LiveData<UserModel> getUser() {
        return userLiveData;
    }

    public void loadUser() {
        UserModel user = new UserModel("Alice", "alice@example.com");
        userLiveData.setValue(user);
    }
}

在这个示例中,UserViewModel 内部使用 MutableLiveData 来存储用户数据。通过 loadUser() 方法,可以在需要的时候调用 setValue() 方法来更新 userLiveData 的值,并通知所有观察者。

总结来说,MutableLiveData 提供了修改数据的能力,而 LiveData 则主要用于观察数据的变化。通常情况下,在 ViewModel 中使用 MutableLiveData 来管理数据,并通过返回 LiveData 来让其他组件观察这些数据。