各位有遇到过 LiveData 在共享 ViewModel 中发生旧数据倒灌的情况吗?

2019-09-23 12:52:56 +08:00
 KunMinX
当我在 作用域 为 Activity 的 共享 ViewModel 中设置了一个 LiveData,

FragmentA observe 这个 LiveData,那么在收到一次通知、并关闭 FragmentA 后,再进入 FragmentA,

由于 该 ViewModel 是共享的,其中的 LiveData 并没有随着 FragmentA 的退出而清除,那么再次进入后,会自动走一遍 observe 回调,倒灌旧数据。

我知道这么设计的缘由是为了处理生命周期从 unActive 到 active 转变时,跟进“最新” 的状态,但在共享 ViewModel 中,这算不算是一个 bug ?因为 LiveData 封装死了,我似乎没有其他办法来干预这种现象的发生。

看看万能的 V 友有没什么好招
10144 次点击
所在节点    Android
30 条回复
bkmi
2019-09-23 13:31:49 +08:00
既然 ViewModel 还在,说明数据还是有效的,并不是什么旧数据。
如果你想把 ViewModel 和 FragmentA 绑定,那作用域就该用 Fragment 的
zyqf
2019-09-23 13:46:19 +08:00
其中的 LiveData 并没有随着 FragmentA 的退出而清除。

我是不是可以理解为你的需求是 FragmentA 退出要清除 LiveData ?

如果是的话,在 FragmentA 销毁阶段 postValue 空对象不就好了。
wsxyeah
2019-09-23 13:56:53 +08:00
类似于 BehaviorSubject
KunMinX
2019-09-23 14:00:26 +08:00
@bkmi @zyqf

使用共享作用域,是为了能够跨页面通信,只不过第一次使用了,第二次它自动就回调了,不符合我预期。

手动 postValue,容易因人工操作的疏忽而造成一致性问题,因为项目有数十个 Fragment,这种事情务必在后台自动完成。

我刚刚找到的线索是,每次进入 fragment,livedata 的 observe.lastVersion 都被赋值为 -1,而 version 仍然是 0,那我觉得 bug 可能就在这里
KunMinX
2019-09-23 14:04:28 +08:00
好消息,找到解决方案了。

通过反射,阻止用户回调的 liveData 第一次创建时的推送。这个推送实在太坑了。

http://www.pianshen.com/article/4994394892/
sanousun
2019-09-23 15:11:52 +08:00
美团有一篇关于 LiveDataBus 的文章,你可以看看 https://tech.meituan.com/2018/07/26/android-livedatabus.html
Gehrman
2019-09-23 17:33:09 +08:00
viewModel 里加多个缓存,跟缓存一样的无视掉?
GLee9507
2019-09-23 17:40:43 +08:00
我认为你是没有真正的理解 ViewModel 和 LiveData
HangoX
2019-09-23 19:23:51 +08:00
其实你直接在 ViewOnCreated 的时候 getLiveData.getValue 拿出来设置就好了,既然是一次性的东西
KunMinX
2019-09-23 20:43:51 +08:00
@HangoX

就用 bus-LiveData 的方案了。

不手动编写的原因,楼上我已经讲过。

不直接用 LiveDataBus 的原因是,需要维持 通过 唯一可信源 分发状态的 开发模式,来避免不可预期、难以追溯和排查的错误。
bkmi
2019-09-23 21:44:03 +08:00
你这是把 LiveData 当 EventBus 用了啊…
KunMinX
2019-09-23 22:06:18 +08:00
我很后悔开了这个贴。

除了 6 楼的 @sanousun 同学给出了建设性的答复,其他人始终慢了好几拍。

我已经把状况讲得这么清楚了。

为避免这些离题的回复 给后来者造成困扰,这里统一回复一下:

任何技术绝非凭空存在,既然存在了,就有存在的缘由。

ViewModel 被如此设计,当初就是有考虑过 使其作为跨页面共享的存在。

当 lifeCyclerOwner 传入的是 fragment 的宿主 Activity,那么这个 ViewModel 就是被该 Activity 所持有、可以被旗下 Fragments 所共享。

共享 ViewModel 的作用多是为了跨页面的通信,例如回调、实时通信等等。

LiveData 的存在是为了确立 通过唯一可信源分发状态 的开发模式,以避免旧时候滥用 EventBus 导致的 难以追溯、数据过时 等问题。
同时在 ViewModel 或单例的配合下,它使单向依赖成为了可能。
再者它也可以规避一些生命周期问题。

这也就是为什么我选择标准化的 Jetpack MVVM 架构来完成状态管理。

除了状态管理,MVVM 同样有这个条件,甚至是更好的条件,来支持消息推送。这也就是为什么我要用 LiveData 来实现跨页面通信,而拒绝使用 EventBus。

而既然考虑到 共享 ViewModel,那么 LiveData 也理应考虑到应解除对默认 粘性设计的支持。

这个粘性设计或许在 视图控制器 重建 恢复状态时有用,在消息通信时则是个麻烦,所以我说这是个 bug,务必解决。
KunMinX
2019-09-23 22:17:23 +08:00
当然,上面我说到了 “ViewModel” 包含共享的考虑,肯定会有杠精不放过一丝添堵的机会。

首先,遇到像 8 楼这种纯粹来恶心人、不就事论事 的评论,我第一个就选择 block,

其次,我可以分享一下自己在项目中是怎么使用 ViewModel 的:

因为 ViewModel 的本职主要是状态的托管,可以托管 DataBinding 或 视图控制器 所依赖的 state,
也即这种 ViewModel 是 fragment 独享的,我会为每个 Fragment 都配一个,反正我有自己编写的自动化脚本工具,配几个 state 字段就能生成对应的 ViewModel。

第二种就是专门用于数据请求,这些我也设计成让他们被 fragment 独占。因为 ViewModel 就是设计为,你传入什么 owner,这个 ViewModel 就是被哪个 owner 所持有。而且反正就算独占,fragment 重建时这个 ViewModel 被 retain 保留了因而可以支持视图状态的恢复。

第三种就是本帖提到的这种消息通信的用法。

我就说这么多了。
GLee9507
2019-09-23 22:18:19 +08:00
@KunMinX #13 没恶心你,说的是实话,真的。
maninfog
2019-09-23 22:43:49 +08:00
@GLee9507 哇 你这种人真的恶心到我😫 本来就是寻求讨论的,你有何高见倒是说出来阿,啥建设性意见没有上来就是一句别人不懂,被怼了还要继续杠,shame on you !
colaman
2019-09-23 22:44:28 +08:00
@KunMinX 我当初是自己写了一个包装类处理这个粘性问题,我是觉得这种属性 Google 应该给开发者自己决定才比较合理
Didadida
2019-09-23 22:53:12 +08:00
试试 SingleLiveEvent ?
mxalbert1996
2019-09-23 22:57:21 +08:00
@KunMinX 说到底就是 LiveData 不是为了你这种用途而设计的。LiveData 相当于 Rx 里的 BehaviorSubject,而你却想把它当 Observable 用。
另外解决这种问题还要用反射也太不优雅了吧。写个新类继承 MutableLiveData,覆写 observe 方法在最前面加一条 setValue(null)不就行了?
zeroDev
2019-09-23 23:02:37 +08:00
@maninfog 网友没有教会你的责任,指出来哪里有问题就是好心了
KunMinX
2019-09-23 23:20:07 +08:00
@colaman

是的。类似的令人头疼的问题也存在于 Navigation。

Navigation 写死了切换 fragment 必须是通过 replace,而不能使用者自己选择,所以当前对于 Navigation 态度,也是保持欣赏但观望的态度(对它的声明式编程理念比较欣赏)。

网上有给出 keep-fragment-state-Navigation 的解法,这里顺带分享一下:

https://github.com/STAR-ZERO/navigation-keep-fragment-sample

https://github.com/lwj1994/navigation-keep-state-fragment

@mxalbert1996

反射的方式影响最小。因为目标只是 为了解决 不自动倒灌 旧数据,要是先 set Null 了,那么其他已观察的页面再想要取 liveData 临时存的数据,就莫得治了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/603309

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX