ViewModel 使用简介
ViewModel 是 Android 提供的一个组件,用于管理与 UI 相关的数据,并在 Activity 或 Fragment 的生命周期变化(如屏幕旋转)时保持数据。ViewModel 主要用于解决数据丢失的问题,特别是在 Activity 或 Fragment 被重新创建时。
ViewModel 是 Android 架构组件(Android Architecture Components)的一部分,它通过与 LiveData 和 Lifecycle 结合使用,可以轻松地管理 UI 数据和响应生命周期的变化。
1. ViewModel 的工作原理
ViewModel 的核心功能是存储和管理 UI 相关的数据,并确保这些数据在配置变化(如屏幕旋转)时不会丢失。具体来说,ViewModel 的工作原理包括以下几个方面:
- 数据存储:ViewModel负责存储与 UI 相关的数据。无论在配置变化还是Activity/Fragment销毁时,ViewModel都会保持数据。
- 生命周期感知:ViewModel与Activity和Fragment的生命周期解耦,它只会在组件生命周期内生效,当组件销毁时,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 在 Activity 或 Fragment 中使用 ViewModel
在 Activity 或 Fragment 中使用 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 处理屏幕旋转
一个常见的场景是,Activity 或 Fragment 在屏幕旋转时会重新创建,并导致 UI 数据丢失。ViewModel 在这种情况下非常有用,因为它不会在 Activity 或 Fragment 重建时被销毁。
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. 使用 ViewModel 和 LiveData 管理 UI 数据
LiveData 是一个生命周期感知的组件,它能确保仅在 Activity 或 Fragment 处于活动状态时才更新 UI,避免了在组件销毁后继续更新 UI 的问题。
3.1 LiveData 和 ViewModel 的结合
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会根据- source1和- source2的数据变化,自动更新其值。
4. ViewModel 和 SavedStateHandle
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 架构组件的一部分,帮助开发者在 Activity 或 Fragment 生命周期内管理 UI 数据,并且在配置变化时保留数据。ViewModel 可以与 LiveData 结合使用,以便响应数据变化并更新 UI。它能够简化应用的生命周期管理,并防止 UI 数据丢失。
- ViewModel主要用于存储和管理 UI 相关的数据。
- LiveData用于提供生命周期感知的数据容器。
- SavedStateHandle用于在- ViewModel中保存动态参数或配置变化时保持的数据。
- Hilt可以与- ViewModel结合使用,为其提供依赖注入。
通过使用 ViewModel 和相关组件,你可以确保 Android 应用的 UI 数据管理更加健壮、灵活且易于维护。
ViewModel 源码解析
ViewModel 是 Android Architecture Components 中的重要组成部分,它用于管理 UI 相关的数据,并在配置更改(如屏幕旋转)时保留这些数据。ViewModel 与 Activity 或 Fragment 的生命周期解耦,确保数据在生命周期变化时不会丢失。
我们来深入分析 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被销毁时清理资源。它会在- Activity或- Fragment销毁时被调用,通常在这里释放资源,如取消网络请求、停止后台任务等。
- clear():- clear()方法用于显式地清除- ViewModel,在调用时会触发- onCleared()。
2. ViewModelProvider 和 ViewModelStore
ViewModel 的生命周期是由 ViewModelProvider 和 ViewModelStore 管理的。ViewModelProvider 用于从 ViewModelStore 中获取 ViewModel 实例,确保每个 ViewModel 在生命周期内仅创建一次。
2.1 ViewModelProvider 类
ViewModelProvider 是用来提供 ViewModel 实例的类。它根据 ViewModelStore 提供不同的存储机制,来确保一个 Activity 或 Fragment 中的 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 的容器,它确保每个 Activity 或 Fragment 有一个 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 的组件(如 Activity、Fragment)都需要实现这个接口。
kotlin
复制编辑
public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}
Activity 和 Fragment 实现了 ViewModelStoreOwner,因此它们可以提供 ViewModelStore,用于管理各自的 ViewModel 实例。
4. ViewModel 与生命周期的关系
ViewModel 是与 Activity 和 Fragment 的生命周期解耦的。当 Activity 或 Fragment 被重新创建(例如屏幕旋转时),ViewModel 会被保留,并且它的数据不会丢失。
ViewModel 的生命周期由 ViewModelProvider 管理。当 Activity 或 Fragment 销毁时,ViewModel 会通过 onCleared() 方法清理资源。
4.1 生命周期的管理
ViewModel 会在以下情况下被销毁:
- Activity或- Fragment完全销毁时,- ViewModel会被销毁。
- 配置变化(如屏幕旋转)时,ViewModel保持不变。
4.2 onCleared() 的调用时机
当 ViewModel 被销毁时,onCleared() 会被调用。在 onCleared() 方法中,开发者可以进行资源释放、取消网络请求、停止后台任务等操作。
kotlin
复制编辑
@Override
public void onCleared() {
    super.onCleared();
    // 释放资源,如取消网络请求、停止任务等
}
5. SavedStateHandle 与 ViewModel
SavedStateHandle 是一个重要的功能,它允许 ViewModel 保持跨配置变化的数据,并在 Activity 或 Fragment 配置变化时恢复状态。
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 相关的数据,确保数据在Activity或Fragment生命周期内的稳定性。
- 生命周期管理:ViewModel与Activity和Fragment的生命周期解耦,避免了数据丢失和内存泄漏的问题。
- ViewModelProvider和- ViewModelStore:这两个类管理- ViewModel的创建和存储,确保每个- Activity或- Fragment只有一个- ViewModel实例。
- onCleared():在- ViewModel被销毁时,- onCleared()方法会被调用,用于清理资源。
- SavedStateHandle:支持在配置变化时保存和恢复数据。
通过 ViewModel 和相关组件,Android 提供了一种高效、简洁的数据管理方案,避免了手动管理数据和生命周期带来的复杂性。