news 2026/10/10 2:40:15

【Jetpack Compose进阶学与练】第3课:ViewModel基础-页面状态管理与生命周期

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Jetpack Compose进阶学与练】第3课:ViewModel基础-页面状态管理与生命周期

前言

前面课程里面我们使用 remember / rememberSaveable 维护页面状态。
但是remember只能保存在Compose重组阶段;屏幕旋转(Activity重建)的时候remember会丢失数据(rememberSaveable有大小限制,不适合大量业务数据)。
ViewModel专门用来存放页面业务数据;生命周期独立于Activity重建;屏幕旋转数据不会丢失;同时配合Compose可以实现状态驱动UI。

前置依赖(module build.gradle)

dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.2" }

知识点清单

  1. ViewModel生命周期特点:屏幕旋转不会销毁;Activity真正销毁时才销毁
  2. viewModel() 获取ViewModel实例;不要自己new
  3. MutableStateFlow + StateFlow 保存可观察业务数据
  4. compose中使用collectAsStateWithLifecycle收集Flow成为界面状态
  5. ViewModel职责:存放业务状态、处理简单业务逻辑;不要持有Compose环境引用,不要持有Context

完整示例源码

importandroidx.lifecycle.ViewModelimportandroidx.lifecycle.viewModelScopeimportkotlinx.coroutines.flow.MutableStateFlowimportkotlinx.coroutines.flow.StateFlowimportkotlinx.coroutines.launchimportandroidx.compose.runtime.Composableimportandroidx.compose.runtime.collectAsStateWithLifecycleimportandroidx.compose.runtime.getValueimportandroidx.compose.ui.Modifierimportandroidx.compose.foundation.layout.Arrangementimportandroidx.compose.foundation.layout.Columnimportandroidx.compose.foundation.layout.fillMaxSizeimportandroidx.compose.material3.Buttonimportandroidx.compose.material3.Scaffoldimportandroidx.compose.material3.TextclassCounterViewModel:ViewModel(){//可变内部数据流,私有privateval_countFlow=MutableStateFlow(0)//对外暴露只读StateFlowvalcountFlow:StateFlow<Int>=_countFlowfunincrease(){viewModelScope.launch{_countFlow.value+=1}}fundecrease(){viewModelScope.launch{_countFlow.value-=1}}}@ComposablefunCounterVmPage(){//获取ViewModel实例,不要手动new CounterViewModel()valvm:CounterViewModel=androidx.lifecycle.viewmodel.compose.viewModel()//在Compose当中收集Flow,感知生命周期valcountbyvm.countFlow.collectAsStateWithLifecycle()Scaffold{innerPadding->Column(modifier=Modifier.fillMaxSize(),verticalArrangement=Arrangement.Center,horizontalAlignment=androidx.compose.ui.Alignment.CenterHorizontally){Text(text="计数值:$count",fontSize=androidx.compose.ui.unit.sp(22))Button(onClick={vm.increase()}){Text("+ 增加")}Button(onClick={vm.decrease()}){Text("- 减少")}}}}

逐行解读

  1. ViewModel生命周期核心特性
    ViewModel对象创建之后;当屏幕发生旋转(Activity销毁重建),ViewModel实例保留不销毁;只有当页面真正finish、Activity彻底关闭的时候ViewModel才会执行clear()销毁。

适合保存比较重的业务数据、网络请求结果;弥补rememberSaveable容量有限的短板。

  1. 获取ViewModel实例
    val vm:CounterViewModel = viewModel()
    ❗禁止手动 new CounterViewModel();交由系统ViewModelProvider管理实例;才能拥有特殊生命周期。
  2. MutableStateFlow / StateFlow
  • MutableStateFlow :可变,ViewModel内部私有修改;
  • StateFlow 只读对外暴露;遵循单向数据流;禁止UI层直接修改ViewModel内部可变Flow;只能调用ViewModel里面定义的函数来变更数据。
  1. viewModelScope
    ViewModel自带协程作用域 viewModelScope ;当ViewModel销毁的时候,内部所有协程会自动取消;不用担心协程内存泄漏;网络请求、延时任务优先写在viewModelScope.launch{}里面。
  2. collectAsStateWithLifecycle()
    收集StateFlow,并且感知Android页面生命周期;页面进入后台暂停收集;回到前台恢复收集;优先使用这个,不要直接使用collectAsState();减少后台无谓重组,节约性能。
  3. ViewModel编码约束(硬性)
    ViewModel里面不要保存Activity、Compose@Composable引用;尽量不要持有Context;如果确实需要上下文,请使用AndroidViewModel。
    ViewModel只负责业务逻辑和状态;不直接操作UI。UI层Compose组件只负责渲染,调用vm的方法触发业务变更。

容易踩坑

  1. ❌ 手动new ViewModel实例;直接丧失ViewModel特殊生命周期,旋转页面数据丢失。
  2. ❌ UI层直接修改 _countFlow.value ;破坏封装;私有可变Flow只能在ViewModel内部修改。
  3. ❌ 在ViewModel直接保存Activity引用,极易发生内存泄漏。
  4. ❌ 使用collectAsState而不是collectAsStateWithLifecycle;页面后台依然持续接收数据,不必要重组,耗电。
  5. ✅ 单向数据流:UI调用ViewModel的方法;ViewModel修改Flow;Flow驱动UI刷新。

多元化习题(每道题后紧跟答案+解读)

习题1【基础‑改错题】

题目:下面创建ViewModel写法错误,请修正

valvm=CounterViewModel()

答案:

valvm:CounterViewModel=androidx.lifecycle.viewmodel.compose.viewModel()

解读:
必须使用compose提供的viewModel()函数,由框架帮我们获取实例;框架管理ViewModel生命周期,屏幕旋转保留数据。自己new出来就是普通类对象,没有ViewModel的生命周期能力。

习题2【基础‑代码修改题】

题目:ViewModel增加reset清零方法,点击之后计数器重置为0;写出ViewModel里面新增函数。
答案:

funreset(){viewModelScope.launch{_countFlow.value=0}}

解读:
在viewModelScope协程块里面修改MutableStateFlow的value;UI层Compose只调用vm.reset(),不需要直接操作Flow。

习题3【进阶‑补充代码题】

题目:说明collectAsStateWithLifecycle的优势;为什么优先选用而不是collectAsState。
答案:
collectAsStateWithLifecycle会结合Android生命周期;当App页面进入后台(页面不可见),暂停收集数据流;页面重新回到前台之后恢复收集;减少后台UI不必要重组,节省CPU和电量;collectAsState不管页面是否可见始终持续接收数据。
解读:
在Compose+Flow开发中,这是官方建议的最佳实践。

习题4【综合‑逻辑应用题】

题目:rememberSaveable 和 ViewModel 都可以解决屏幕旋转数据丢失;二者分别适合什么场景?
答案:
rememberSaveable:适合少量简单轻量状态(开关标记,输入框简短文本);有数据大小限制;数据会序列化保存到Bundle。
ViewModel:适合较重业务数据;列表数据、网络加载结果;没有大小限制;依靠对象内存保存;进程被杀之后数据依然会丢失。
解读:
进程彻底杀掉之后,ViewModel内存数据一样丢失;这种场景依然需要本地持久化(数据库、SP)。

习题5【概念简答题】

题目:什么是单向数据流(ViewModel‑Compose)?
答案:
UI层不直接修改业务状态;UI触发事件调用ViewModel内部业务函数;ViewModel内部修改StateFlow;Flow发出新的值;驱动上层Compose UI重组刷新界面;数据单向流动。
解读:
单向数据流是现代声明式UI开发重要原则;减少状态随意修改带来的难以复现bug。

本节课知识点总结

  1. ViewModel生命周期独立;屏幕旋转Activity重建时ViewModel对象保留;Activity真正销毁才调用clear()销毁。
  2. 使用 viewModel() 获取实例,禁止手动new;viewModelScope提供自动管理的协程作用域。
  3. MutableStateFlow私有,对外暴露只读StateFlow;单向数据流;UI调用ViewModel函数修改状态。
  4. compose收集Flow优先 collectAsStateWithLifecycle() ,感知生命周期,优化后台性能。
  5. ViewModel禁止持有Activity引用,防止内存泄漏;ViewModel负责业务;Compose负责渲染UI。

下一课预告:进阶第4课:ViewModel结合Navigation导航;不同页面之间共享ViewModel

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 2:39:51

文献管理怎么下手?按检索、归档、标签、调用四个环节把工具配齐

文献管理卡住人的地方&#xff0c;通常不是软件挑得不对&#xff0c;而是顺序没排清。把它拆成检索、归档、标签、调用四段&#xff0c;每段只配一件顺手的工具&#xff0c;链条就通了。知学术AIPaperGPT 把文献检索、自建文献库与大纲写作放在同一条链路上&#xff0c;适合作为…

作者头像 李华
网站建设 2026/10/10 2:39:34

Linux:操作系统基础

一、Linux是什么&#xff1f;Linux 是一套免费、开源的类 Unix 操作系统&#xff0c;由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;于 1991 年首次发布。二、操作系统操作系统&#xff1a;管理计算机底层硬件的和软件资源的系统软件处理在硬件和上层应用中间&am…

作者头像 李华
网站建设 2026/10/10 2:39:32

图文并茂手把手教你使用Claude Code 配置定时任务以及自动发邮件

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华