You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用传参自定义工厂的navGraphViewModels获取共享ViewModel

问题根因

崩溃的核心原因是Kotlin属性委托的懒加载特性:你在群组列表页用by navGraphViewModels(R.id.chat) { factory }声明ViewModel时,只是注册了委托规则,只要在跳转前没有主动访问过这个chatVM属性,委托逻辑就不会触发,导航图对应的ViewModel存储区里根本不会生成MessagesViewModel实例。
等跳转到消息页后,消息页调用navGraphViewModels(R.id.chat)时没有传入自定义工厂,系统查找不到现成实例,就会调用默认的无参构造工厂尝试创建实例,自然就抛出没有零参构造函数的异常。

可行解决方案

方案1:最小改动版,跳转前提前触发VM初始化

如果你的消息页只有从群组列表跳转这一个入口,只需要在列表页执行导航动作前,主动访问一次声明的chatVM属性,触发委托完成实例创建即可:

val factory = MessagesViewModelFactory(item.group.id!!)
val chatVM: MessagesViewModel by navGraphViewModels(R.id.chat) { factory }

// 主动访问VM触发初始化,将实例存入导航图作用域
chatVM // 直接访问属性即可,不需要额外调用方法

val action = MessageGroupsFragmentDirections.actionNavigationSettingsToMessagesFragment()
findNavController().navigate(action)

消息页原有代码不需要任何修改,直接获取就能拿到已经创建好的同实例ViewModel。
注意:这个方案存在路径依赖,如果后续新增其他入口直接打开消息页,没有经过列表页的初始化逻辑,依然会触发崩溃。

方案2:稳妥版,通过导航参数传递群组ID,双端统一工厂逻辑

这是生产环境更推荐的实现方式,不依赖前一个页面的初始化逻辑,从根源避免异常:

  • 首先在导航配置文件中,给MessagesFragment添加groupId类型的导航参数,和你的群组ID实际类型匹配
  • 列表页跳转时将群组ID作为参数传入:
val action = MessageGroupsFragmentDirections
    .actionNavigationSettingsToMessagesFragment(groupId = item.group.id!!)
findNavController().navigate(action)
  • 消息页获取ViewModel时,先从导航参数中取出群组ID,构造对应的自定义工厂传入即可:
// 从导航参数取当前页面绑定的群组ID
val args: MessagesFragmentArgs by navArgs()
val viewModel: MessagesViewModel by navGraphViewModels(R.id.chat) {
    MessagesViewModelFactory(args.groupId)
}

这种写法下,不管是列表页还是消息页第一次访问这个导航图作用域的ViewModel,都会用带正确群组ID的工厂创建实例,后续两个页面访问到的都是同一个共享实例,既满足VM共享的需求,也不会出现入口依赖导致的崩溃。


内容的提问来源于stack exchange,提问作者Msencenb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:51:23