具有共享视图模型的Android导航组件
viewmodel随活动或其所附加的片段而存在和消亡。这有一定的影响,我无法理解为什么没有人问(如果我们把导航架构融入图片中) 根据最新的android博客和导航框架的工作方式,我们建议使用单活动多片段模式 假设我有以下应用程序设计具有共享视图模型的Android导航组件,android,android-architecture-components,android-jetpack,Android,Android Architecture Components,Android Jetpack,viewmodel随活动或其所附加的片段而存在和消亡。这有一定的影响,我无法理解为什么没有人问(如果我们把导航架构融入图片中) 根据最新的android博客和导航框架的工作方式,我们建议使用单活动多片段模式 假设我有以下应用程序设计 Activity A (Application Entry Point) ---------- Fragment (A) (Uses ViewModel AB) Fragment (B) (Uses ViewModel AB) Fragment (C) (Uses
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)
现在,由于我使用共享viewmodels,这意味着我的viewmodels将附加到活动。然而,这似乎是有漏洞的。比如,如果我已经从A到E一路遍历,现在又从一个片段跳到另一个片段,那么viewmodel CDE应该被销毁,但它不会被销毁,因为它连接到活动
此外,我们无法将viewmodels连接到片段,因为我们将共享它们的数据
只有我提出这个问题的事实让我相信我的理解是错误的。如果能给我一个正确的了解情况的机会,我会很高兴的。我想这是你的问题: 就像我从A到E一路走过,现在又回来了 从片段弹出到片段B,viewmodel CDE应该是 已销毁,但不会被销毁,因为它已连接到活动 您希望使用ViewModel在多个片段之间共享数据,但希望确保片段导航到特定屏幕时,ViewModel的数据将Destroy 我的建议解决方案是:
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()
}
}
clear()
功能的ViewModelStore中。但是,清除会从ViewModelStore中清除所有模型,这在您的情况下是不需要的(ViewModel AB和ViewModel CDE都将从活动的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 = this@MainActivity.getIndividualViewModelStore<VIEWMODEL>()
return ViewModelProvider(this.getIndividualViewModelStore<VIEWMODEL>(), factory).get(VIEWMODEL::class.java)
}
val viewModelStore = this@MainActivity.getIndividualViewModelStore<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)
}
稍后,当需要处理共享ViewModel时,请使用clearIndividualViewModelStore()
:
这确实是一个问题,而且一直存在 幸运的是,自从
Navigation 2.1.0-alpha02
(在2.1.0
中稳定)以来,这个问题已经解决了。您可以找到更改日志和
现在,您可以通过Kotlin用户的by navGraphViewModels()
属性委托,或使用添加到NavController
的getViewModelStore()
API,创建范围在导航图级别的视图模型
首先,您应该在nav graph designer中选择一些片段,然后右键单击它们并选择移动到嵌套图
,以创建一个新的图,该图将用作如下“范围”:
class DetailFr : Fragment() {
private val vm: DetailViewModel by navGraphViewModels(R.id.main_nav_graph)
}
您可以了解有关嵌套图的更多信息。或者,如果您无法执行建议的解决方案,只需在
活动
级别清除视图模型
,共享视图模型
的作用域
您可以这样做:
getActivity().getViewModelStore().clear();
这将确保共享视图模型的数据被清除。由于
导航2.1.0-alpha02
(在2.1.0
中稳定),您可以通过通过navGraphViewModels()
创建具有导航图级别范围的视图模型
要使ViewModel不附加到活动或单个片段,必须创建嵌套的导航图,并在该图的范围内请求ViewModel的实例。
这将导致当您在嵌套导航图中时,ViewModel将处于活动状态,并且嵌套图中的片段将重用ViewModel的同一实例
通过这种方式,您可以有几个嵌套的导航图,每个导航图都有一个ViewModel实例,这些实例将在组成该图的片段之间共享
我将遵循相同的片段和视图模型分布:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (BuildConfig.DEBUG) {
navController.addOnDestinationChangedListener { _, _, _ ->
if (individualModelStores.isNotEmpty()) {
val tag = this@MainActivity.javaClass.simpleName
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 ${this@MainActivity.javaClass.simpleName}:"
)
for ((index, viewModelClass) in individualModelStores.keys.withIndex()) {
Log.w(
tag,
"${index + 1}) $viewModelClass\n"
)
}
}
}
}
}
MainActivity (Application Entry Point)
----------
Fragment (A) (Uses SharedViewModelOne) -> navGraphOne
Fragment (B) (Uses SharedViewModelOne) -> navGraphOne
Fragment (C) (Uses SharedViewModelTwo) -> navGraphTwo
Fragment (D) (Uses SharedViewModelTwo) -> navGraphTwo
要实现这一点,您必须遵循以下步骤:
...
apply plugin: 'kotlin-kapt'
android {
...
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies{
...
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
kapt 'androidx.lifecycle:lifecycle-compiler:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
}
移动到嵌套图
在本例中,我将FragmentA和FragmentB添加到navGraphOne和FragmentC中,碎片D到导航图WO 查找有关嵌套导航图的详细信息
private val modelOne: SharedViewModelOne by navGraphViewModels(R.id.navGraphOne) {
//defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
defaultViewModelProviderFactory
}
override fun onCreateView(
..
): View? {
...
//use binding.lifecycleOwner = viewLifecycleOwner
//to make sure the observer disappears when the fragment is destroyed
modelOne.item.observe(viewLifecycleOwner, Observer {
//do Something
})
...
}
private val modelTwo: SharedViewModelTwo by navGraphViewModels(R.id.navGraphTwo) {
//defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
defaultViewModelProviderFactory
}
override fun onCreateView(
..
): View? {
...
//use binding.lifecycleOwner = viewLifecycleOwner
//to make sure the observer disappears when the fragment is destroyed
modelTwo.item.observe(viewLifecycleOwner, Observer {
//do Something
})
...
}
onCleared()
方法,并在ViewModel的init{}
中添加检查点
例如:
class SharedViewModelOne : ViewModel() {
private val _item = MutableLiveData<String>()
val item : LiveData<String>
get() = _item
init {
Log.d(TAG, "SharedViewModelOne has created!")
}
override fun onCleared() {
super.onCleared()
Log.d(TAG, "SharedViewModelOne has removed!")
}
}
class-SharedViewModelOne:ViewModel(){
private val_item=MutableLiveData()
val项目:LiveData
获取()=\u项
初始化{
L
private val modelOne: SharedViewModelOne by navGraphViewModels(R.id.navGraphOne) {
//defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
defaultViewModelProviderFactory
}
override fun onCreateView(
..
): View? {
...
//use binding.lifecycleOwner = viewLifecycleOwner
//to make sure the observer disappears when the fragment is destroyed
modelOne.item.observe(viewLifecycleOwner, Observer {
//do Something
})
...
}
private val modelTwo: SharedViewModelTwo by navGraphViewModels(R.id.navGraphTwo) {
//defaultViewModelProviderFactory or the ViewModelProvider.Factory you are using.
defaultViewModelProviderFactory
}
override fun onCreateView(
..
): View? {
...
//use binding.lifecycleOwner = viewLifecycleOwner
//to make sure the observer disappears when the fragment is destroyed
modelTwo.item.observe(viewLifecycleOwner, Observer {
//do Something
})
...
}
class SharedViewModelOne : ViewModel() {
private val _item = MutableLiveData<String>()
val item : LiveData<String>
get() = _item
init {
Log.d(TAG, "SharedViewModelOne has created!")
}
override fun onCleared() {
super.onCleared()
Log.d(TAG, "SharedViewModelOne has removed!")
}
}
private val myViewModel by lazy {
requireParentFragment().childFragmentManager.primaryNavigationFragment?.getViewModel<MyViewModel>()
}