Android ViewModel(Kotlin):ViewModel的数据共享范围及跨组件访问疑问
首先可以明确告诉你:你遇到的这个现象是正常的,核心原因在于ViewModel的实例是和「作用域所有者(ViewModelStoreOwner)」绑定的——简单说就是,你调用ViewModelProviders.of(xxx)时传入的xxx是谁,就决定了这个ViewModel属于谁,哪些组件能共享它。
咱们拆解你的场景来分析:
为什么Activity和Fragment拿到的ViewModel值不一样?
你在Activity中用ViewModelProviders.of(this)获取ViewModel,这里的this是当前Activity本身(它是一个ViewModelStoreOwner),所以你得到的是专属这个Activity的ViewModel实例,给它设result=6后,这个值只会存在于这个实例里。
而如果你的Fragment A和Fragment B在获取ViewModel时,用的是ViewModelProviders.of(this)(也就是把Fragment自身作为作用域所有者),那每个Fragment都会拥有自己独立的MyViewModel实例。但你说Fragment A设了result=9后,Fragment B能看到这个值,说明这两个Fragment用的是同一个作用域所有者——比如它们是同一个父Fragment的子Fragment,或者你在Fragment里用了父Fragment作为作用域。但不管怎样,这个实例和Activity的实例是完全分开的,所以Activity看到的还是6,Fragment看到的是9。
关于ViewModel的共享范围,你需要理清这几点:
- Activity的ViewModel并非只对其他Activity可见:恰恰相反,Activity的ViewModel可以被所有以该Activity作为作用域所有者的Fragment共享。比如如果你的Fragment里用
ViewModelProviders.of(requireActivity())获取ViewModel,那得到的实例和Activity里的是同一个,数据会完全同步——这也是Activity和Fragment共享数据最常用的方式。 - Fragment的ViewModel也不是仅对其他Fragment可见:如果Fragment用自身作为作用域(
of(this)),那这个ViewModel只有它自己和它的子Fragment能共享;但如果它用Activity作为作用域,那就能和Activity、其他同属该Activity的Fragment共享。
实现跨组件共享的正确方式
完全不需要用Bundle、Intent这类方式,只要让需要共享数据的组件使用同一个ViewModelStoreOwner来获取ViewModel就行:
- 要让Activity + 所有子Fragment共享:Activity里用
ViewModelProvider(this)[MyViewModel::class.java],Fragment里用ViewModelProvider(requireActivity())[MyViewModel::class.java](注意:ViewModelProviders已经被废弃,现在推荐用ViewModelProvider)。 - 要让多个兄弟Fragment共享(不和Activity共享):可以用它们的父Fragment作为作用域,也就是在Fragment里调用
ViewModelProvider(requireParentFragment())[MyViewModel::class.java]。 - 如果是跨不同Activity共享数据:ViewModel就无能为力了(因为每个Activity都是独立的作用域所有者),这时候可以用单例、Room数据库、SharedPreferences或者Jetpack DataStore这类持久化/全局状态方案。
举个正确的共享例子,假设你想让Activity和Fragment共享同一个ViewModel:
// Activity中获取 val model = ViewModelProvider(this)[MyViewModel::class.java] model.result = 6 // Fragment中获取(和Activity共享) val model = ViewModelProvider(requireActivity())[MyViewModel::class.java] // 这里获取到的result就是6,之后如果Fragment修改了值,Activity也能看到最新值
内容的提问来源于stack exchange,提问作者iperion300

