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 提供了一种高效、简洁的数据管理方案,避免了手动管理数据和生命周期带来的复杂性。