ViewModel 是 Android 架构组件之一,用于管理 UI 相关的数据,并在 配置更改(如屏幕旋转)时保持数据不丢失


🔹 1. 添加 ViewModel 依赖

build.gradle.kts(模块级)中添加:

1
2
3
dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
}

🔹 2. 创建 ViewModel

(1)基础 ViewModel

1
2
3
4
5
6
7
8
9
import androidx.lifecycle.ViewModel

class CounterViewModel : ViewModel() {
var count = 0 // ViewModel 变量(Activity 重建后不会丢失)

fun increment() {
count++
}
}

🔹 3. 在 Activity/Fragment 中使用

(1)Activity 获取 ViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import android.os.Bundle
import android.widget.TextView
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
private val counterViewModel: CounterViewModel by viewModels() // 通过 viewModels() 获取 ViewModel

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

val textView: TextView = findViewById(R.id.counterText)
val button: Button = findViewById(R.id.incrementButton)

// 显示当前计数
textView.text = counterViewModel.count.toString()

// 点击按钮增加计数
button.setOnClickListener {
counterViewModel.increment()
textView.text = counterViewModel.count.toString()
}
}
}

即使旋转屏幕,count 也不会重置


🔹 4. ViewModel + LiveData 监听数据变化

使用 LiveDataUI 会自动更新

(1)ViewModel(使用 LiveData

1
2
3
4
5
6
7
8
9
10
11
12
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class CounterViewModel : ViewModel() {
private val _count = MutableLiveData(0) // 可变 LiveData
val count: LiveData<Int> = _count // 只暴露不可变 LiveData

fun increment() {
_count.value = (_count.value ?: 0) + 1 // 更新数据
}
}

(2)Activity 观察 LiveData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MainActivity : AppCompatActivity() {
private val counterViewModel: CounterViewModel by viewModels()

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

val textView: TextView = findViewById(R.id.counterText)
val button: Button = findViewById(R.id.incrementButton)

// 观察 LiveData,自动更新 UI
counterViewModel.count.observe(this) { newCount ->
textView.text = newCount.toString()
}

button.setOnClickListener {
counterViewModel.increment()
}
}
}

LiveData 确保数据变化时,UI 会自动更新


🔹 5. 处理 ViewModel 依赖(带参数的 ViewModel)

(1)ViewModel 需要参数

如果 ViewModel 需要参数(如 Repository),需要创建 ViewModelProvider.Factory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CounterViewModel(private val startValue: Int) : ViewModel() {
private val _count = MutableLiveData(startValue)
val count: LiveData<Int> = _count

fun increment() {
_count.value = (_count.value ?: 0) + 1
}
}

// 自定义 ViewModelFactory
class CounterViewModelFactory(private val startValue: Int) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(CounterViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return CounterViewModel(startValue) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

(2)Activity 获取带参数的 ViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MainActivity : AppCompatActivity() {
private val counterViewModel: CounterViewModel by viewModels {
CounterViewModelFactory(5) // 传入初始值 5
}

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

val textView: TextView = findViewById(R.id.counterText)
val button: Button = findViewById(R.id.incrementButton)

counterViewModel.count.observe(this) { newCount ->
textView.text = newCount.toString()
}

button.setOnClickListener {
counterViewModel.increment()
}
}
}

支持带参数的 ViewModel(如从数据库或网络获取初始数据)


🔹 6. ViewModel + Room 持久化数据库数据

如果你使用 Room 存储数据,ViewModel 可以直接从 Room 获取 LiveData

1
2
3
4
5
6
7
8
9
@Dao
interface DiaryDao {
@Query("SELECT * FROM diary_entries ORDER BY date DESC")
fun getAllDiaries(): LiveData<List<DiaryEntry>> // LiveData 监听数据库
}

class DiaryViewModel(private val diaryDao: DiaryDao) : ViewModel() {
val diaryList: LiveData<List<DiaryEntry>> = diaryDao.getAllDiaries()
}

Fragment 观察数据:

1
2
3
diaryViewModel.diaryList.observe(viewLifecycleOwner) { diaryList ->
diaryAdapter.submitList(diaryList) // 更新 UI
}

Room + ViewModel + LiveData 确保数据持久化,并自动更新 UI


🔹 7. ViewModel + Kotlin Flow(协程)

如果你用 Flow,可以这样:

1
2
3
4
class DiaryViewModel(private val diaryDao: DiaryDao) : ViewModel() {
val diaryListFlow = diaryDao.getAllDiariesFlow()
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) // 转为 StateFlow
}

Fragmentcollect 数据:

1
2
3
4
5
lifecycleScope.launch {
viewModel.diaryListFlow.collect { diaryList ->
diaryAdapter.submitList(diaryList)
}
}

Flow 更适合复杂数据流(如数据库、网络请求)


🔹 8. ViewModel 生命周期

场景 ViewModel 作用
旋转屏幕 保持数据不丢失
App 进后台 ViewModel 可能被销毁(可用 SavedStateHandle 处理)
Fragment 切换 共享 ViewModel 以保留数据

🔹 总结

ViewModel 用于管理 UI 相关数据,避免因屏幕旋转导致数据丢失
✅ 搭配 LiveData 自动通知 UI 变化
ViewModel + Room 可以自动监听数据库变化
ViewModel + Flow 适合复杂数据流处理
ViewModelFactory 用于创建带参数的 ViewModel