Android ViewModel用法详解

598 阅读2分钟

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


🔹 1. 添加 ViewModel 依赖

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

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

🔹 2. 创建 ViewModel

(1)基础 ViewModel

import androidx.lifecycle.ViewModel
​
class CounterViewModel : ViewModel() {
    var count = 0 // ViewModel 变量(Activity 重建后不会丢失)
​
    fun increment() {
        count++
    }
}

🔹 3. 在 Activity/Fragment 中使用

(1)Activity 获取 ViewModel

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

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

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

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

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

@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 观察数据:

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

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


🔹 7. ViewModel + Kotlin Flow(协程)

如果你用 Flow,可以这样:

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

Fragmentcollect 数据:

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