如何通过ViewModel正确实现Fragment间的数据共享?
关于observe回调外无法获取值的说明
LiveData的observe方法注册的观察者是异步触发的,只有当关联的生命周期所有者(当前场景下是Fragment的视图生命周期)走到STARTED及以上状态时,才会收到数据回调。你在注册观察者后立刻打印sharedString,此时回调还没有执行,所以拿到的还是初始空值,只有在回调内部才能保证拿到LiveData发送的值。
1. 两种取值方式的选择
- Fragment B 更适合用观察的方式:因为B需要感知从C返回后的数据修改,只要观察LiveData,每次数据变化都会自动收到回调,无需手动处理页面返回的取值逻辑,能保证拿到的始终是最新值。
- Fragment C 如果仅需要单次读取当前值做修改,直接取
value是可行的,但必须做好空判断,禁止直接用!!强转。如果C页面也需要响应数据的动态变化,依然要使用观察的方式。
补充:当前场景更推荐使用导航图范围的ViewModel,替代你现在用的Activity范围ViewModel,只要将ViewModel的作用域绑定到三个Fragment所属的导航图即可,导航流程结束后ViewModel会自动销毁,不会长期占用Activity的内存。
2. 偶现崩溃的原因
崩溃的核心原因是空指针,你的代码中多处强行使用!!处理可空类型,只要出现空值就会直接抛出异常:
- ViewModel的
share方法中,你直接对可空参数share执行!!强转,如果调用时传入null,这里直接触发NPE。 - LiveData的
value本身是可空类型,就算你泛型声明为非空的String,未赋值时value依然为null,你在Fragment C中直接写share.shared.value!!,空值时就会崩溃。 - 你没有给
MutableLiveData设置初始值,在还没有调用share方法赋值的情况下,观察者被触发时拿到的it就是null,直接使用也会触发空指针。
3. 实现的问题与遗漏要点
你当前的实现有多处不规范的地方,优化点如下:
- 不要直接暴露
MutableLiveData:应该封装为不可变的LiveData对外提供,避免外部随意修改内部数据,参考实现:
class ShareViewModel : ViewModel() { private val _sharedContent = MutableLiveData<String>() val sharedContent: LiveData<String> = _sharedContent fun updateContent(content: String?) { // 空值直接忽略,或者设置默认值 content?.let { _sharedContent.value = it } } }
- 禁止滥用
!!操作符:所有读取可空值的地方都要做兼容处理,比如在C中取值可以写成val currentStr = share.sharedContent.value ?: "默认值",避免空指针。 - 优先使用更轻量的传参方案:正向传参(A到B、B到C)可以直接用Navigation的Safe Args实现,返回数据(C到B)可以用Fragment Result API,不需要所有场景都依赖Activity范围的ViewModel,逻辑更清晰也避免内存冗余。
- 如果坚持用ViewModel做数据共享,优先选择导航图范围的ViewModel,仅在当前导航流程内共享数据,不会和Activity内其他页面的逻辑冲突。
内容的提问来源于stack exchange,提问作者Mark Delphi
相关产品推荐
相关产品推荐

