四、ViewModel + StateFlow + 状态持久化

四、ViewModel + StateFlow + 状态持久化

将状态从Composable组件中分离出来,单独处理数据。

UI与数据处理逻辑分离

今天的内容比较少(主要是今天有点忙 -。-!)

1、为什么需要ViewModel

  • ViewModel生命周期创建于Activity启动,销毁与Activity真正的finish,不会因为中途状态改变而被销毁重建(例如屏幕旋转,切换页面等)
  • 状态分散在多个Composable中,难以复用测试。
  • 无法在多个Composable中共享状态。
  • 要做到数据与UI的分离,让逻辑更加清晰,代码结构更加健壮。

2、StateFlow是什么

  • 是Kotlin协程中的热流(Hot Flow)

  • 始终持有最新状态值

  • 支持多个观察者

  • 完美适配Compose的状态驱动UI

    KOTLIN 复制代码
    //用于ViewModel内部更新数据
    private val _uiState = MutableStateFlow(NoteUIState(isLoading = true))
    // 只读,暴露给UI读取
    val uiState: StateFlow<NoteUiState> = _uiState.asStateFlow() 

    MutableStateFlow → 内部修改

    StateFlow → 外部只读(封装性)

3、collectAsState():链接Flow与Compose

kotlin 复制代码
val uiState by viewModel.uiState.collectAsState()
  • 自动在Composable进入时收集(collect)Flow
  • 自动在退出时取消收集
  • 当Flow发出新值时 -> 触发重组(Recomposition)

4、项目完整结构重构

  • 相关依赖包(第一篇中已经全部导入)

    kotlin 复制代码
    dependencies {
        implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
        implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
    }
  • 定义UI状态数据类

    kotlin 复制代码
    data class NoteUIState(
        val notes:List<Note> = emptyList(),
        val isLoading: Boolean = false,
        val error:String? = null
    )
  • 创建NoteViewModel类

    kotlin 复制代码
    package com.jiahengfei.ktdm
    
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.flow.MutableStateFlow
    import kotlinx.coroutines.flow.StateFlow
    import kotlinx.coroutines.flow.asStateFlow
    import kotlinx.coroutines.launch
    
    /**
     * Create by zFox from AndroidStudio2022.01
     * 日期:2026/1/8 20:01
     * 描述:ViewModel数据模型类
     */
    class NoteViewModel: ViewModel() {
        private val _uiState = MutableStateFlow(NoteUIState(isLoading = true))
        val uiState: StateFlow<NoteUIState> = _uiState.asStateFlow()
    
        init {
            loadNotes()
        }
    
        private fun loadNotes() {
            viewModelScope.launch {
                delay(2000)
                val mockNotes = listOf(
                    Note(1, "协程进阶", "学会了 ViewModel + StateFlow", "2026-01-08", listOf("Compose")),
                    Note(2, "状态管理", "UI 状态现在由 ViewModel 驱动", "2026-01-08", listOf("MVVM")),
                    Note(3, "协程进阶2", "学会了 ViewModel + StateFlow", "2026-01-08", listOf("Compose")),
                    Note(4, "状态管理2", "UI 状态现在由 ViewModel 驱动", "2026-01-08", listOf("MVVM"))
                )
    
                _uiState.value = NoteUIState(
                    notes = mockNotes,
                    isLoading = false
                )
            }
        }
    }
  • 更新笔记列表NoteListScreen,使用ViewModel管理状态

    kotlin 复制代码
    package com.jiahengfei.ktdm
    
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.PaddingValues
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.lazy.LazyColumn
    import androidx.compose.foundation.lazy.items
    import androidx.compose.material3.CircularProgressIndicator
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.collectAsState
    import androidx.compose.runtime.getValue
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.unit.dp
    import androidx.lifecycle.viewmodel.compose.viewModel
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.withContext
    
    /**
     * Create by zFox from AndroidStudio2022.01
     * 日期:2026/1/7 19:10
     * 描述:笔记本列表
     */
    
    /**
     * 挂起函数
     */
    suspend fun requestNotes(){
        return withContext(Dispatchers.IO){
            delay(2000)
        }
    }
    
    @Composable
    fun NoteListScreen(
        modifier: Modifier = Modifier,
        showSnackBar: (String) -> Unit,
        viewModel: NoteViewModel = viewModel()
    ) {
        val uiState by viewModel.uiState.collectAsState()
    
        Box(
            modifier = modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            if (uiState.isLoading) {
                CircularProgressIndicator()
            } else {
                //显示笔记本列表
                LazyColumn(
                    modifier = Modifier.fillMaxSize(),
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                    contentPadding = PaddingValues(16.dp)
                ) {
                    items(uiState.notes) { note ->
                        NoteCard(
                            note = note,
                            showSnackBar,
                            modifier = Modifier.fillParentMaxWidth()
                        )
                    }
                }
            }
        }
    }
  • 启动查看结果

这就是第四天的全部内容啦,明天我们将切换到实战项目进行编写边学。

相关推荐
bytebitx1 天前
MacOS 编译腾讯 Mars XLog 及使用(已16KB对齐)
kotlin·mac·apk
alexhilton2 天前
让架构边界变成可执行的测试
android·kotlin·android jetpack
数据治理自习室2 天前
AI 应用评测体系(GraphRAG)
android·大数据·人工智能·kotlin
名剑走天下3 天前
Android 面试题大全
android·kotlin
JMchen1233 天前
Jetpack 内核实战(十二):总结——架构思维 + 面试高频考点 + 后续路线
room·viewmodel·android 面试·jetpack 总结·架构师成长·android 进阶
chjif3 天前
Android 设备管控开发实战:自定义 Launcher 如何只显示指定 App?
android·kotlin
非凡ghost3 天前
用 Kotlin 和 Jetpack Compose 重写的安卓视频播放器,原生体验有多流畅?
android·java·kotlin·音视频·软件需求
名剑走天下4 天前
android 面试题
kotlin
蜡台4 天前
Jetpack Compose 稳定性、重组优化(Stable / @NonRestartableComposable)
android·kotlin·compose·jepack
JMchen1234 天前
Jetpack 内核实战(十):实战(下)——编辑页、全局设置与内存泄漏排查
kotlin·内存泄漏·android 实战·datastore 设置·jetpack 项目优化·编辑页开发