Muh*_*lib 5 android android-architecture-components android-jetpack
视图模型与活动或与其相连的片段一起生存和死亡。这具有一定的影响,这超出了我的范围,为什么没有人问(如果我们将Navigation体系结构引入图片中)。
根据最新的android博客和导航框架的工作方式,建议我们使用Single Activity Multiple Fragments一节。
据说我有以下应用程序设计。
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)
Run Code Online (Sandbox Code Playgroud)
现在,由于我使用共享的视图模型,这意味着我的视图模型将附加到活动中。但是,这似乎是泄漏的。就像我从A一直遍历到E,然后又从片段弹出到片段B一样,视图模型CDE应该被销毁,但是不会被破坏,因为它已连接到活动。
同样,我们将无法将视图模型连接到片段,因为我们将共享它们的数据。
只有我提出这个问题的事实使我相信,根据我的理解,我在这里是错误的。如果我能对情况有一个适当的了解,那会很高兴。
Ale*_*ort 11
由于Navigation 2.1.0-alpha02(稳定在2.1.0),您可以通过在导航图级别创建具有范围的 ViewModelby navGraphViewModels().
要使 ViewModel 不附加到活动或单个片段,您必须创建嵌套导航图并在该图的范围内请求 ViewModel 的实例。这将导致当您在嵌套导航图中时,ViewModel 将继续存在,并且嵌套图中的片段将重用 ViewModel 的相同实例。
通过这种方式,您可以拥有多个嵌套导航图,每个导航图都有一个 ViewModel 实例,该实例将在构成该图的片段之间共享。
我将遵循您相同的片段和 ViewModel 分布:
MainActivity (Application Entry Point)
----------
Fragment (A) (Uses SharedViewModelOne) -> navGraphOne
Fragment (B) (Uses SharedViewModelOne) -> navGraphOne
Fragment (C) (Uses SharedViewModelTwo) -> navGraphTwo
Fragment (D) (Uses SharedViewModelTwo) -> navGraphTwo
Run Code Online (Sandbox Code Playgroud)
为此,您必须执行以下步骤:
你的 build.gradle(Module) 应该是这样的
...
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'
}
Run Code Online (Sandbox Code Playgroud)
选择将共享相同 ViewModel 的片段并将它们添加到嵌套导航图中。为此,请在导航图设计器中选择片段,然后右键单击它们并选择Move to Nested Graph
在这个例子中,我将 FragmentA 和 Fragment B 添加到 navGraphOne 和 FragmentC 和
Fragment D 添加到 navGraphTwo。
在此处查找有关嵌套导航图的更多信息
在 Fragment A 和 Fragment B 中,请求 SharedViewModelOne 的实例。
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
})
...
}
Run Code Online (Sandbox Code Playgroud)
在 Fragment C 和 Fragment D 中,请求 SharedViewModelTwo 的实例。
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
})
...
}
Run Code Online (Sandbox Code Playgroud)
然后要验证是否仅创建了 ViewModel 的单个实例并且它在片段之间共享,请覆盖该onCleared()方法并在init{}在 ViewModel 的 中。
例如:
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!")
}
}
Run Code Online (Sandbox Code Playgroud)
按照前面的步骤操作后,您应该能够创建一个 ViewModel,该视图模型将在属于同一嵌套导航图的片段之间共享,该图表示 ViewModel 只会在您处于图形内部时才存在,如果您离开它,它将是摧毁了。
如果你觉得有些事情对你来说不是很清楚,你可以查看这个repo并澄清你的疑虑。
这确实是一个问题,已报告给Google。
幸运的是Navigation 2.1.0-alpha02,2.1.0此问题(在中稳定)已经得到解决。您可以在此处和文档中找到更改日志。
现在,您可以通过
by navGraphViewModels()Kotlin用户的属性委托或使用getViewModelStore()添加到的API 创建在导航图级别范围内的ViewModelNavController。
首先,您应该在导航图设计器中选择一些片段,然后右键单击它们,然后选择Move to Nested Graph创建一个新的图,将其用作“范围”,如下所示:
class DetailFr : Fragment() {
private val vm: DetailViewModel by navGraphViewModels(R.id.main_nav_graph)
}
Run Code Online (Sandbox Code Playgroud)
您可以Nested Graph 在此处了解更多信息。
我以为这是你的问题:
就像我从 A 一直遍历到 E 现在回来将片段弹出到片段 B 一样,视图模型 CDE 应该被销毁,但它不会被销毁,因为它已连接到活动。
您希望使用 ViewModel 在多个 Fragments 之间共享数据,但您希望确保当 Fragment 导航到特定屏幕时ViewModel 的数据将被销毁。
我对此的建议解决方案是:
在 ViewModel 类中创建一个销毁数据函数,该函数将通过将其值覆盖为空值(例如“” )来销毁 ViewModel 的数据
class CDEViewModel : ViewModel() {
var dataString: String = ""
fun destroyViewModelData() { // Function that will Destroythe Data
dataString= ""
}
}
Run Code Online (Sandbox Code Playgroud)现在,每当您需要确保 ViewModel 数据被清除/销毁时,您都可以在 Fragment 中调用destroyViewModelData函数
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()
}
}
Run Code Online (Sandbox Code Playgroud)在您的情况下,您可以在 FragmentE 的 onStop() 处调用destroyViewModelData 函数,因此当您从 FragmentE 导航到 FragmentB时, CDEViewModel 的数据全部变为“”空字符串,这意味着它已被Reset/Destroy。
希望这个简单的解决方案可以有所帮助。谢谢。
| 归档时间: |
|
| 查看次数: |
1140 次 |
| 最近记录: |