视图模型与活动或附加的片段一起生存和死亡。这有一定的后果,超出我的原因是没有人询问(如果我们将导航架构纳入图片中)。
根据最新的Android博客和导航框架的工作方式,我们建议进入Single Activity Multiple Fragments verse。
据说我有以下应用程序设计。
Activity A (Application Entry Point)
----------
Fragment (A) (Uses ViewModel AB)
Fragment (B) (Uses ViewModel AB)
Fragment (C) (Uses ViewModel CDE)
Fragment (D) (Uses ViewModel CDE)
Fragment (E) (Uses ViewModel CDE)
现在,因为我使用共享的视图模型,这意味着我的视图模型将附加到活动。然而,这似乎是漏洞。就像我从A到E一直遍历并现在回来弹出片段到片段B一样,视图模型CDE应该被销毁,但它不会因为它连接到活动。
此外,我们无法将视图模型连接到片段,因为我们将共享其数据。
事实上,只有我提出这个问题,才能让我相信我的理解是错误的。如果能够对这种情况给予适当的了解,我会很高兴。
这真的是一个问题,一直是reported to Google。
幸运的是,自Navigation 2.1.0-alpha02
这个问题已经解决了。你可以找到更改日志here和document。
您现在可以通过kotlin用户的
by navGraphViewModels()
属性委托或使用添加到getViewModelStore()
的NavController
API创建在导航图级别确定范围的ViewModel。
首先,您应该在导航图设计器中选择一些片段,然后右键单击它们并选择Move to Nested Graph
以创建一个新图形,它将用作“范围”,如下所示:
class DetailFr : Fragment() {
private val vm: DetailViewModel by navGraphViewModels(R.id.main_nav_graph)
}
您可以了解更多关于Nested Graph
here的信息。
我以为这是你的问题:
就像我从A到E一直遍历并现在回来弹出片段到片段B一样,视图模型CDE应该被销毁,但它不会因为它连接到活动。
您希望使用ViewModel在多个片段之间共享数据,但是您希望确保在片段导航到某个屏幕时ViewModel的数据将被销毁。
我的建议解决方案是:
class CDEViewModel : ViewModel() {
var dataString: String = ""
fun destroyViewModelData() { // Function that will Destroythe Data
dataString= ""
}
}
class FragmentE {
private lateinit var cdeViewModel : CDEViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize your ViewModel
cdeViewModel = ViewModelProviders.of(this).get(CDEViewModel ::class.java)
}
override fun onStart() {
super.onStart()
// Set your Value Here
cdeViewModel.dataString = "String 1"
}
override fun onStop() {
super.onStop()
// Reset/Destroy Data when Screen is Being Close/Navigate to other Screen
// After Call this function, in Whatever Screen, the ViewModel previous Set ""String 1"" Data is Clear/Destroy and become "" empty value.
cdeViewModel.destroyViewModelData()
}
}
在你的情况下,你可以在FragmentE的onStop()上调用destroyViewModelData函数,所以当你从FragmentE导航到FragmentB时,CDEViewModel的数据都变成“”空字符串,这意味着它已经被重置/销毁。
希望这个简单的解决方案有所帮谢谢。
每个LifecycleOwner(即片段或活动)都将其模型保存在具有clear()
函数的ViewModelStore中。但是,清除扫描ViewModelStore中的所有模型,在您的情况下是不合需要的(ViewModel AB和ViewModel CDE都将从Activity的ViewModelStore中清除)。此问题的一种可能解决方案是拥有可在必要时安全清除的每个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 viewModelCDE : ViewModelCDE = (requireActivity() as MainActivity).getSharedViewModel(/*There could be some arguments in case of a more complex ViewModelProvider.Factory implementation*/)
稍后,当处理共享的ViewModel时,请使用clearIndividualViewModelStore<>()
:
(requireActivity() as MainActivity).clearIndividualViewModelStore<ViewModelCDE>()
在某些情况下,如果不再需要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"
)
}
}
}
}
}