参考android.arch.lifecycle.ViewModel
类。
ViewModel
的范围涉及它所涉及的UI组件的生命周期,因此在基于Fragment
的应用程序中,这将是片段生命周期。这是一件好事。
在某些情况下,人们想要在多个片段之间共享ViewModel
实例。具体而言,我对许多屏幕与相同基础数据相关的情况感兴趣。
(当多个相关片段显示在同一屏幕上但this can be worked around by using a single host fragment as per answer below时,文档提出了类似的方法。)
这在official ViewModel documentation中讨论过:
ViewModels还可以用作活动的不同片段之间的通信层。每个Fragment都可以通过其Activity使用相同的密钥获取ViewModel。这允许以解耦方式在片段之间进行通信,使得它们永远不需要直接与其他片段对话。
换句话说,要在表示不同屏幕的片段之间共享信息,ViewModel
应该限定为Activity
生命周期(根据Android文档,这也可以在其他共享实例中使用)。
现在,在新的Jetpack导航模式中,建议使用“One Activity / Many Fragments”架构。这意味着活动在应用程序使用的整个过程中都存在。
即任何限定为ViewModel
生命周期的共享Activity
实例永远不会被清除 - 内存仍然在不断使用。
为了保留内存并在任何时间点使用尽可能少的内容,能够在不再需要时清除共享的ViewModel
实例会很好。
如何从它的ViewModel
或持有者片段手动清除ViewModelStore
?
如果你检查代码here你会发现,你可以从ViewModelStore
和ViewModelStoreOwner
,Fragment
获得FragmentActivity
,例如,实现,接口。
从那里你可以打电话给viewModelStore.clear()
,正如文件所说:
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
N.B。:这将清除特定LifeCycleOwner的所有可用ViewModel,这不允许您清除一个特定的ViewModel。
如果您不希望将ViewModel
限定为Activity
生命周期,则可以将其范围限定为父片段的生命周期。因此,如果要在屏幕中与多个片段共享ViewModel
的实例,则可以布置片段,使它们共享一个共同的父片段。这样,当您实例化ViewModel
时,您可以这样做:
CommonViewModel viewModel = ViewModelProviders.of(getParentFragment()).class(CommonViewModel.class);
希望这有帮助!
我想我有更好的解决方案。
正如@Nagy Robi所说,你可以通过电话ViewModel
清除viewModelStore.clear()
。这个问题是它将清除此ViewModelStore
范围内的所有视图模型。换句话说,你无法控制清除哪个ViewModel
。
但根据@mikehc here。我们实际上可以创建我们自己的ViewModelStore
。这将允许我们精确控制ViewModel必须存在的范围。
注意:我没有看到有人这样做,但我希望这是一个有效的方法。这将是控制单个活动应用程序中的范围的一种非常好的方法。
请提供一些有关此方法的反馈。任何事情都会受到赞赏。
从Navigation Component v2.1.0-alpha02开始,ViewModel
s现在可以用于流量。这样做的缺点是你必须为你的项目实施Navigation Component
,而且你对ViewModel
的范围没有任何粒度控制。但这似乎是一件好事。
我只是写文库来解决这个问题:scoped-vm,随时查看它,我将非常感谢任何反馈。在引擎盖下,它使用了@Archie提到的方法 - 它为每个范围维护单独的ViewModelStore。但是,只要请求来自该范围的viewmodel的最后一个片段破坏,它就会更进一步清除ViewModelStore本身。
我应该说,目前整个视图模型管理(特别是这个lib)受到带有backstack的serious bug的影响,希望它将被修复。
摘要:
ViewModel.onCleared()
没有被调用,最好的方法(现在)是自己清除它。由于这个bug,你无法保证fragment
的viewmodel将被清除。ViewModel
- 不用担心,它们将被垃圾收集为任何其他非引用对象。如果它适合您的需要,请随意使用我的lib进行细粒度范围界定。正如指出的那样,使用体系结构组件API无法清除ViewModelStore的单个ViewModel。此问题的一种可能解决方案是拥有可在必要时安全清除的每个ViewModel存储:
class MainActivity : AppCompatActivity() {
val individualModelStores = HashMap<KClass<out ViewModel>, ViewModelStore>()
inline fun <reified VIEWMODEL : ViewModel> getSharedViewModel(): VIEWMODEL {
val factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
//Put your existing ViewModel instantiation code here,
//e.g., dependency injection or a factory you're using
//For the simplicity of example let's assume
//that your ViewModel doesn't take any arguments
return modelClass.newInstance()
}
}
val viewModelStore = [email protected]<VIEWMODEL>()
return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
}
val viewModelStore = [email protected]<VIEWMODEL>()
return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
}
inline fun <reified VIEWMODEL : ViewModel> getIndividualViewModelStore(): ViewModelStore {
val viewModelKey = VIEWMODEL::class
var viewModelStore = individualModelStores[viewModelKey]
return if (viewModelStore != null) {
viewModelStore
} else {
viewModelStore = ViewModelStore()
individualModelStores[viewModelKey] = viewModelStore
return viewModelStore
}
}
inline fun <reified VIEWMODEL : ViewModel> clearIndividualViewModelStore() {
val viewModelKey = VIEWMODEL::class
individualModelStores[viewModelKey]?.clear()
individualModelStores.remove(viewModelKey)
}
}
使用getSharedViewModel()
获取绑定到Activity生命周期的ViewModel实例:
val yourViewModel : YourViewModel = (requireActivity() as MainActivity).getSharedViewModel(/*There could be some arguments in case of a more complex ViewModelProvider.Factory implementation*/)
稍后,当处理共享的ViewModel时,请使用clearIndividualViewModelStore<>()
:
(requireActivity() as MainActivity).clearIndividualViewModelStore<YourViewModel>()
在某些情况下,如果不再需要ViewModel,则需要尽快清除ViewModel(例如,如果它包含一些敏感的用户数据,如用户名或密码)。这是一种在每个片段切换时记录individualModelStores
状态的方法,以帮助您跟踪共享的ViewModel:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (BuildConfig.DEBUG) {
navController.addOnDestinationChangedListener { _, _, _ ->
if (individualModelStores.isNotEmpty()) {
val tag = [email protected]
Log.w(
tag,
"Don't forget to clear the shared ViewModelStores if they are not needed anymore."
)
Log.w(
tag,
"Currently there are ${individualModelStores.keys.size} ViewModelStores bound to ${[email protected]}:"
)
for ((index, viewModelClass) in individualModelStores.keys.withIndex()) {
Log.w(
tag,
"${index + 1}) $viewModelClass\n"
)
}
}
}
}
}
通常,您不会手动清除ViewModel,因为它是自动处理的。如果您觉得需要手动清除ViewModel,那么您可能在ViewModel中做了太多...
使用多个视图模型没有任何问题。第一个可以作为活动的范围,而另一个可以作用于片段。
尝试仅将Activity范围的Viewmodel用于需要共享的内容。并在Fragment Scoped Viewmodel中放置尽可能多的东西。片段被销毁时,将清除Fragment范围的视图模型。减少整体内存占用。