一、ViewModel 概述
ViewModel 是 Android Architecture Components 中的一个核心类,它的设计目的是存储和管理与 UI 相关的数据,并且在配置变更(如屏幕旋转)时保持数据的存活。ViewModel 的生命周期比 Activity 或 Fragment 更长,这使得它成为存储 UI 状态的理想选择。
ViewModel 的基本用法
以下是一个简单的 ViewModel 示例:
kotlin
class MyViewModel : ViewModel() {
private val _userData = MutableLiveData<User>()
val userData: LiveData<User> = _userData
init {
loadUserData()
}
private fun loadUserData() {
// 模拟从网络或数据库加载数据
_userData.value = User("John Doe", 30)
}
}
在 Activity 或 Fragment 中使用 ViewModel:
kotlin
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
viewModel.userData.observe(this) { user ->
// 更新 UI
}
}
}
二、ViewModel 的生命周期
ViewModel 的生命周期与 Activity 或 Fragment 的生命周期不同。当 Activity 或 Fragment 被销毁时,ViewModel 不会立即被销毁,而是会继续存在,直到 Activity 或 Fragment 的生命周期完全结束。
生命周期对比图
事件 | Activity/Fragment 状态 | ViewModel 状态 |
---|---|---|
Activity/Fragment 创建 | 已创建 | 已创建 |
配置变更(如旋转屏幕) | 销毁并重建 | 保持不变 |
Activity/Fragment 完全销毁 | 已销毁 | 已销毁 |
三、ViewModel 的销毁时机
1. Activity 的销毁时机
对于 Activity,ViewModel 会在 Activity 完成其生命周期并被系统彻底销毁时被销毁。这通常发生在用户按下返回按钮或调用 finish()
方法时。
示例代码:
kotlin
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
// 使用 ViewModel
}
override fun onDestroy() {
super.onDestroy()
// 检查 Activity 是否正在被销毁(而不是因为配置变更)
if (!isChangingConfigurations) {
// Activity 正在被彻底销毁
// ViewModel 将很快被销毁
}
}
}
2. Fragment 的销毁时机
对于 Fragment,ViewModel 的销毁时机略有不同。当 Fragment 从 Activity 中移除时,如果 Activity 仍然存在(例如,Fragment 被替换但 Activity 未被销毁),ViewModel 不会被销毁。只有当 Activity 本身被销毁时,Fragment 的 ViewModel 才会被销毁。
示例代码:
kotlin
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 初始化视图
return inflater.inflate(R.layout.fragment_my, container, false)
}
override fun onDestroyView() {
super.onDestroyView()
// 视图被销毁,但 ViewModel 仍然存在
// 除非 Fragment 所属的 Activity 也被销毁
}
}
3. 配置变更时的行为
当发生配置变更(如屏幕旋转)时,Activity 和 Fragment 会被销毁并重新创建,但 ViewModel 会保持不变。这是 ViewModel 的一个重要特性,它允许数据在配置变更期间保持存活。
示例代码:
kotlin
class MyViewModel : ViewModel() {
private val _counter = MutableLiveData<Int>()
val counter: LiveData<Int> = _counter
init {
_counter.value = 0
}
fun incrementCounter() {
_counter.value = (_counter.value ?: 0) + 1
}
}
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
viewModel.counter.observe(this) { count ->
// 更新 UI,即使在旋转屏幕后也会保持正确的值
}
}
}
4. 手动清除 ViewModel
在某些特殊情况下,你可能需要手动清除 ViewModel。虽然这种情况很少见,但可以通过 ViewModelStore
来实现:
kotlin
// 手动清除 ViewModel
viewModelStore.clear()
但请注意,这通常不是必需的,因为系统会在适当的时候自动处理 ViewModel 的生命周期。
四、ViewModel 销毁时的资源释放
当 ViewModel 被销毁时,如果你有需要释放的资源(如网络连接、文件句柄等),可以重写 onCleared()
方法:
kotlin
class MyViewModel : ViewModel() {
private val job = Job()
override fun onCleared() {
super.onCleared()
// 释放资源
job.cancel()
}
}
五、常见问题与最佳实践
1. 内存泄漏问题
由于 ViewModel 的生命周期比 Activity 和 Fragment 长,因此在 ViewModel 中持有 Activity 或 Fragment 的引用可能会导致内存泄漏。
错误示例:
kotlin
class MyViewModel : ViewModel() {
private var activity: Activity? = null // 错误!可能导致内存泄漏
fun setActivity(activity: Activity) {
this.activity = activity // 不要这样做
}
}
正确做法:
kotlin
class MyViewModel : ViewModel() {
// 使用 Application 上下文(如果需要上下文)
private lateinit var application: Application
constructor(application: Application) : super() {
this.application = application
}
}
2. 最佳实践总结
- 不要在 ViewModel 中持有 Activity、Fragment 或 View 的引用
- 使用
onCleared()
方法释放资源 - 优先使用 LiveData 或 StateFlow 来管理 UI 状态
- 在 Activity 的
onDestroy()
或 Fragment 的onDestroyView()
中避免访问 ViewModel,因为此时 ViewModel 可能已经被销毁
六、总结
ViewModel 的销毁时机与 Activity 和 Fragment 的生命周期密切相关,但又有所不同。理解这些差异对于正确使用 ViewModel 至关重要。通过合理管理 ViewModel 的生命周期,你可以确保数据在配置变更期间保持存活,同时避免内存泄漏和资源浪费。