Android-开源框架和源码分析-09-ViewModel-源码解析

400 阅读9分钟

ViewModel 使用简介

ViewModel 是 Android 提供的一个组件,用于管理与 UI 相关的数据,并在 ActivityFragment 的生命周期变化(如屏幕旋转)时保持数据。ViewModel 主要用于解决数据丢失的问题,特别是在 ActivityFragment 被重新创建时。

ViewModel 是 Android 架构组件(Android Architecture Components)的一部分,它通过与 LiveDataLifecycle 结合使用,可以轻松地管理 UI 数据和响应生命周期的变化。


1. ViewModel 的工作原理

ViewModel 的核心功能是存储和管理 UI 相关的数据,并确保这些数据在配置变化(如屏幕旋转)时不会丢失。具体来说,ViewModel 的工作原理包括以下几个方面:

  • 数据存储ViewModel 负责存储与 UI 相关的数据。无论在配置变化还是 Activity/Fragment 销毁时,ViewModel 都会保持数据。
  • 生命周期感知ViewModelActivityFragment 的生命周期解耦,它只会在组件生命周期内生效,当组件销毁时,ViewModel 会被自动清除。
  • 避免内存泄漏ViewModel 保证了与 UI 组件解耦,避免了 UI 组件与数据的直接引用,减少了内存泄漏的风险。

2. ViewModel 基本用法

2.1 创建 ViewModel

ViewModel 主要通过构造函数注入数据源(例如网络请求或数据库操作)。你可以将任何需要的依赖注入到 ViewModel 中。

kotlin
复制编辑
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> get() = _data

    fun loadData() {
        _data.value = "Hello, World!"
    }
}
  • LiveData 是一个生命周期感知的数据容器,用于在数据变化时更新 UI。
  • ViewModel 保持了与 UI 相关的数据,并保证在配置变化时数据不会丢失。

2.2 在 ActivityFragment 中使用 ViewModel

ActivityFragment 中使用 ViewModel,你可以通过 ViewModelProvider 获取 ViewModel 实例。使用 by viewModels() 可以简化获取 ViewModel 的代码。

kotlin
复制编辑
class MainActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 观察数据
        viewModel.data.observe(this, Observer { newData ->
            // 更新 UI
            textView.text = newData
        })

        // 加载数据
        viewModel.loadData()
    }
}
  • by viewModels():通过该属性委托获取 ViewModel 实例,Hilt 或 ViewModelProvider 会自动注入 ViewModel
  • observe()LiveData 会观察数据的变化,并在数据更新时自动更新 UI。

2.3 使用 ViewModel 处理屏幕旋转

一个常见的场景是,ActivityFragment 在屏幕旋转时会重新创建,并导致 UI 数据丢失。ViewModel 在这种情况下非常有用,因为它不会在 ActivityFragment 重建时被销毁。

kotlin
复制编辑
class MyViewModel : ViewModel() {
    private val _counter = MutableLiveData<Int>()
    val counter: LiveData<Int> get() = _counter

    init {
        _counter.value = 0
    }

    fun incrementCounter() {
        _counter.value = _counter.value?.plus(1)
    }
}

在屏幕旋转后,ViewModel 会自动恢复数据,避免丢失。

kotlin
复制编辑
class MainActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 观察数据
        viewModel.counter.observe(this, Observer { value ->
            // 更新 UI
            textView.text = value.toString()
        })

        // 增加计数
        incrementButton.setOnClickListener {
            viewModel.incrementCounter()
        }
    }
}
  • 屏幕旋转:当旋转屏幕时,MainActivity 会重新创建,但 ViewModel 中的数据(如 counter)会保留下来。

3. 使用 ViewModelLiveData 管理 UI 数据

LiveData 是一个生命周期感知的组件,它能确保仅在 ActivityFragment 处于活动状态时才更新 UI,避免了在组件销毁后继续更新 UI 的问题。

3.1 LiveDataViewModel 的结合

LiveData 通常与 ViewModel 一起使用,它能够存储 ViewModel 中的可观察数据,并在数据变化时自动通知 UI 更新。

kotlin
复制编辑
class MyViewModel : ViewModel() {
    private val _userData = MutableLiveData<User>()
    val userData: LiveData<User> get() = _userData

    fun loadUserData() {
        // 模拟网络请求
        _userData.value = User("John", "Doe")
    }
}

data class User(val firstName: String, val lastName: String)

Activity 中观察 LiveData

kotlin
复制编辑
class MainActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel.userData.observe(this, Observer { user ->
            // 更新 UI
            userNameTextView.text = "${user.firstName} ${user.lastName}"
        })

        viewModel.loadUserData()
    }
}
  • LiveData 会在数据变化时通知 UI 更新,而不会在 Activity 销毁后继续更新 UI。

3.2 使用 MediatorLiveData 组合多个 LiveData

MediatorLiveData 是一个特殊的 LiveData 类型,可以将多个 LiveData 合并成一个。通过 addSource() 方法,你可以将其他 LiveData 的变化合并并发送到观察者。

kotlin
复制编辑
class MyViewModel : ViewModel() {
    private val _source1 = MutableLiveData<Int>()
    private val _source2 = MutableLiveData<Int>()

    val combinedData: LiveData<Int> = MediatorLiveData<Int>().apply {
        addSource(_source1) { value = _source1.value!! + _source2.value!! }
        addSource(_source2) { value = _source1.value!! + _source2.value!! }
    }

    fun updateData() {
        _source1.value = 10
        _source2.value = 20
    }
}
  • combinedData 会根据 source1source2 的数据变化,自动更新其值。

4. ViewModelSavedStateHandle

SavedStateHandle 用于在 ViewModel 中保存动态参数或在配置变化时保留数据。它提供了一个存储和恢复数据的机制,特别适合处理需要在配置变化后保持的数据。

kotlin
复制编辑
class MyViewModel @HiltViewModel constructor(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val _data = savedStateHandle.getLiveData<String>("data_key")

    fun updateData(newData: String) {
        _data.value = newData
        savedStateHandle.set("data_key", newData)
    }
}
  • SavedStateHandle 可以在 ViewModel 中使用,以便在配置变化(如屏幕旋转)时保留数据。

Activity 中使用:

kotlin
复制编辑
class MainActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel.updateData("New Data")

        viewModel._data.observe(this, Observer { newData ->
            textView.text = newData
        })
    }
}

5. ViewModel 和 Hilt 的集成

Hilt 可以自动注入 ViewModel 的依赖,使得 ViewModel 的构造函数更简洁。使用 @HiltViewModel@Inject 注解,Hilt 会为 ViewModel 提供所需的依赖。

kotlin
复制编辑
@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: MyRepository
) : ViewModel() {
    fun getData() {
        // 使用 repository 获取数据
    }
}

@HiltViewModel 注解标记的 ViewModel 会与 Hilt 结合,自动注入依赖。


6. 总结

ViewModel 是 Android 架构组件的一部分,帮助开发者在 ActivityFragment 生命周期内管理 UI 数据,并且在配置变化时保留数据。ViewModel 可以与 LiveData 结合使用,以便响应数据变化并更新 UI。它能够简化应用的生命周期管理,并防止 UI 数据丢失。

  • ViewModel 主要用于存储和管理 UI 相关的数据。
  • LiveData 用于提供生命周期感知的数据容器。
  • SavedStateHandle 用于在 ViewModel 中保存动态参数或配置变化时保持的数据。
  • Hilt 可以与 ViewModel 结合使用,为其提供依赖注入。

通过使用 ViewModel 和相关组件,你可以确保 Android 应用的 UI 数据管理更加健壮、灵活且易于维护。

ViewModel 源码解析

ViewModel 是 Android Architecture Components 中的重要组成部分,它用于管理 UI 相关的数据,并在配置更改(如屏幕旋转)时保留这些数据。ViewModelActivityFragment 的生命周期解耦,确保数据在生命周期变化时不会丢失。

我们来深入分析 ViewModel 的源码,了解它是如何工作的,以及它如何与 Android 生命周期相关联。


1. ViewModel 类的基本结构

ViewModel 类位于 androidx.lifecycle 包中,它的核心功能是提供与 UI 相关的数据管理,并确保数据在组件生命周期内的存活。

1.1 ViewModel 类声明

kotlin
复制编辑
public class ViewModel {
    private boolean mCleared = false;

    /**
     * This is the main method used to handle lifecycle-related data management.
     */
    public void onCleared() {
        // 清理 ViewModel 资源时被调用
    }

    /**
     * This is the method to clear data explicitly.
     */
    protected void clear() {
        if (!mCleared) {
            onCleared();  // 清理操作
            mCleared = true;
        }
    }
}
  • onCleared()ViewModel 中的 onCleared() 方法用于在 ViewModel 被销毁时清理资源。它会在 ActivityFragment 销毁时被调用,通常在这里释放资源,如取消网络请求、停止后台任务等。
  • clear()clear() 方法用于显式地清除 ViewModel,在调用时会触发 onCleared()

2. ViewModelProviderViewModelStore

ViewModel 的生命周期是由 ViewModelProviderViewModelStore 管理的。ViewModelProvider 用于从 ViewModelStore 中获取 ViewModel 实例,确保每个 ViewModel 在生命周期内仅创建一次。

2.1 ViewModelProvider

ViewModelProvider 是用来提供 ViewModel 实例的类。它根据 ViewModelStore 提供不同的存储机制,来确保一个 ActivityFragment 中的 ViewModel 实例是唯一的。

kotlin
复制编辑
public class ViewModelProvider {
    private final ViewModelStore mViewModelStore;
    private final Factory mFactory;

    public ViewModelProvider(ViewModelStore store, Factory factory) {
        mViewModelStore = store;
        mFactory = factory;
    }

    public <T extends ViewModel> T get(Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(modelClass);
        if (viewModel == null) {
            viewModel = mFactory.create(modelClass);
            mViewModelStore.put(modelClass, viewModel);
        }
        return (T) viewModel;
    }
}
  • get()get() 方法用于获取指定类型的 ViewModel。如果 ViewModel 实例已经存在于 ViewModelStore 中,它会直接返回该实例,否则会通过 Factory 创建新的 ViewModel 实例。

2.2 ViewModelStore

ViewModelStore 是一个存储 ViewModel 的容器,它确保每个 ActivityFragment 有一个 ViewModel 存储区域,用于保存 ViewModel 实例。

kotlin
复制编辑
public class ViewModelStore {
    private final Map<Class<? extends ViewModel>, ViewModel> mMap = new HashMap<>();

    public <T extends ViewModel> T get(Class<T> modelClass) {
        return (T) mMap.get(modelClass);
    }

    public <T extends ViewModel> void put(Class<T> modelClass, T viewModel) {
        mMap.put(modelClass, viewModel);
    }
}
  • get() :返回与 modelClass 类型匹配的 ViewModel 实例。
  • put() :将 ViewModel 存储到 ViewModelStore 中。

3. ViewModelStoreOwner 接口

ViewModelStoreOwner 是一个接口,提供 ViewModelStore 的获取方法。所有需要管理 ViewModel 的组件(如 ActivityFragment)都需要实现这个接口。

kotlin
复制编辑
public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

ActivityFragment 实现了 ViewModelStoreOwner,因此它们可以提供 ViewModelStore,用于管理各自的 ViewModel 实例。


4. ViewModel 与生命周期的关系

ViewModel 是与 ActivityFragment 的生命周期解耦的。当 ActivityFragment 被重新创建(例如屏幕旋转时),ViewModel 会被保留,并且它的数据不会丢失。

ViewModel 的生命周期由 ViewModelProvider 管理。当 ActivityFragment 销毁时,ViewModel 会通过 onCleared() 方法清理资源。

4.1 生命周期的管理

ViewModel 会在以下情况下被销毁:

  • ActivityFragment 完全销毁时,ViewModel 会被销毁。
  • 配置变化(如屏幕旋转)时,ViewModel 保持不变。

4.2 onCleared() 的调用时机

ViewModel 被销毁时,onCleared() 会被调用。在 onCleared() 方法中,开发者可以进行资源释放、取消网络请求、停止后台任务等操作。

kotlin
复制编辑
@Override
public void onCleared() {
    super.onCleared();
    // 释放资源,如取消网络请求、停止任务等
}

5. SavedStateHandleViewModel

SavedStateHandle 是一个重要的功能,它允许 ViewModel 保持跨配置变化的数据,并在 ActivityFragment 配置变化时恢复状态。

5.1 SavedStateHandle 使用

SavedStateHandle 提供了一种在配置变化时保存和恢复数据的机制。

kotlin
复制编辑
class MyViewModel @HiltViewModel constructor(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    fun getData(): String {
        // 从 SavedStateHandle 获取数据
        return savedStateHandle["data_key"] ?: "Default Data"
    }

    fun setData(data: String) {
        // 将数据保存到 SavedStateHandle
        savedStateHandle["data_key"] = data
    }
}
  • SavedStateHandle 可以存储数据,并在配置变化时恢复这些数据。
  • 通过 SavedStateHandle 可以轻松管理跨配置变化的数据。

6. 总结

ViewModel 是 Android 中用于管理 UI 相关数据的组件,旨在确保数据在配置变化(如屏幕旋转)时不丢失。ViewModel 的核心功能包括:

  • 数据存储ViewModel 用于存储 UI 相关的数据,确保数据在 ActivityFragment 生命周期内的稳定性。
  • 生命周期管理ViewModelActivityFragment 的生命周期解耦,避免了数据丢失和内存泄漏的问题。
  • ViewModelProviderViewModelStore:这两个类管理 ViewModel 的创建和存储,确保每个 ActivityFragment 只有一个 ViewModel 实例。
  • onCleared() :在 ViewModel 被销毁时,onCleared() 方法会被调用,用于清理资源。
  • SavedStateHandle:支持在配置变化时保存和恢复数据。

通过 ViewModel 和相关组件,Android 提供了一种高效、简洁的数据管理方案,避免了手动管理数据和生命周期带来的复杂性。