Jetpack Compose深链加载页面时ViewModel数据短暂显示后为空
问题成因
该问题由两个核心问题共同触发,常规导航路径下两个问题不会触发异常,仅在深链的特殊导航流程中暴露:
- MutableState更新方式不符合Compose快照机制规则
mutableStateOf默认仅追踪value指向的对象引用变更,直接修改value内部字段的操作不会触发状态变更通知,也不会被快照系统持久记录。
常规应用内导航是单步直达目标页,无额外的状态对齐流程,这种不规范的写法可以碰巧正常渲染;但深链加载时,Navigation组件会先执行一次占位用的初始composition,待返回栈重建、深链参数校验绑定完成后,会触发全局状态快照对齐,这类未触发引用变更的内部字段修改会被直接回滚到状态初始值,表现为数据短暂显示后消失。 - LaunchedEffect key设置错误
页面内使用LaunchedEffect(key1 = Unit)绑定数据加载逻辑,固定值key无法感知深链流程中的NavBackStackEntry替换:深链跳转时会先后生成临时占位entry、绑定正确参数的正式entry,临时entry被销毁替换时,由于key始终是固定值Unit,Compose会复用效应实例,不会在正式entry挂载时重新执行数据加载逻辑,快照回滚后无逻辑重新写入数据。
修复方案
按以下步骤调整即可解决问题:
- 修正MutableState的更新逻辑,确保状态变更符合快照系统规则
优先使用不可变数据类,更新状态时整体替换value引用,这是Compose官方推荐的状态实现方式:
如果必须使用可变Java类/普通可变类,需要给需要被状态追踪的字段单独套// 将JobModel定义为不可变数据类 data class JobModel( val jobTitle: String = "" ) class CompanyViewModel : ViewModel() { // 使用by委托简化写法,限制外部修改权限 var job by mutableStateOf(JobModel()) private set fun getJob(jobPostUid: String, onComplete: () -> Unit) { // 通过copy整体更新状态,触发引用变更通知 job = job.copy(jobTitle = "New Title") onComplete() } }mutableStateOf,不要仅在最外层包裹:class JobModel { // 字段单独加状态追踪 var jobTitle by mutableStateOf("") } class CompanyViewModel : ViewModel() { // 外层不需要再套mutableStateOf val job = JobModel() fun getJob(jobPostUid: String, onComplete: () -> Unit) { job.jobTitle = "New Title" onComplete() } } - 修正LaunchedEffect的key,不要使用固定值Unit,绑定页面唯一标识(即路由传入的index参数),确保entry替换时数据加载逻辑可以正常重新执行:
// 以路由参数index作为效应key,参数变化、entry重建时都会重新执行逻辑 LaunchedEffect(key1 = index) { companyVm.getJob(index) {} } - 可选加固:通过SavedStateHandle在ViewModel中直接获取路由参数,不要从Composable层透传,彻底规避深链参数传递时机不一致的问题:
class CompanyViewModel( savedStateHandle: SavedStateHandle ) : ViewModel() { // 直接从导航状态存储中取参数,不受composition时机影响 private val index: String = savedStateHandle["index"] ?: "" // 其余状态、业务逻辑不变 }
内容的提问来源于stack exchange,提问作者JScott
相关产品推荐
相关产品推荐

