我应该独立于 Fragment 的生命周期使用 ViewModelScope 吗?
Should I use ViewModelScope independent of Fragment's lifecycle?
TL;DR
对于片段的一个实例,即一次又一次地 added/removed,其 ViewModel 中的 viewModelScope
仅在第一次弹出片段之前有效。此后,viewModelScope
变为非活动状态。添加回片段时如何重新初始化 viewModelScope
?或者我应该寻找更好的实现来保持它的活跃吗?
场景:
我在旧项目中使用 RxJava
到 ViewModel
从网络检索数据,并将更新发送到 UI,我使用了 LiveData。
以下是我在片段中初始化典型 ViewModel 的方式:
private val fruitsViewModel by viewModels<FruitsViewModel>()
以下是上面提到的ViewModel:
class FruitsViewModel: ViewModel {
private var category: String = FruitCategories.DEFAULT
private val _fruits = MutableLiveData<List<Fruit>>()
fun fruits(): LiveData<List<Fruit>> = _fruits
fun updateCategory(newCategory: String) {
this.category = newCategory
fetchFruits()
}
fun fetchFruits() { ... }
}
在Fragment的onViewCreated()
中,我调用了fruitsViewModel.fetchFruits()
。这很好用(没有任何明显的问题)。
今天,我用协程切换了 RxJava 特定代码,以在我的 ViewModel 中检索网络数据。像这样:
fun fetchFruits() {
/* this.fruitsJob = */
viewModelScope.launch {
...
}
}
这也很好用,即使在我替换片段并返回后也是如此。但是,如果我 保留 在 Activity 中声明的片段实例并再次使用它,viewModelScope.launch {}
将不再起作用。像这样:
class FruitsActivity: AppCompatActivity() {
private val fruitsFragment = FruitsFragment()
private fun showFruitsFragment() {
supportFragmentManager
.beginTransaction()
.replace(getFragmentContainerId(), fruitsFragment)
.addToBackStack(fruitsFragment.tag)
.commit()
}
private fun removeFragments() {
val manager = supportFragmentManager
if (manager.backStackEntryCount > 0) {
val first: FragmentManager.BackStackEntry = manager.getBackStackEntryAt(0)
manager.popBackStack(first.id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
}
}
保留片段实例的原因是只加载一次API数据,之后,它应该只在点击按钮时出现,没有任何flicker/loading。
在我调用 removeFragments()
之后,fruitsViewModel
已经调用了它的 onCleared()
用于 kept 声明 fruitsFragment
。因此,它的 viewModelScope
变为非活动状态,如果我通过调用 showFruitsFragment()
再次添加 fruitsFragment
,这将不再检索网络数据。我的 Rxjava 实现不是这种情况,因为我没有使用 viewModelScope
.
我想我可以通过将 fruitsViewModel
链接到 FruitsActivity
的实例来解决问题。但是,我想知道这是否可以被认为是最好的方法。
是的,您应该以不同的方式确定 viewModel
的范围,在您的情况下,将 fruitsViewModel
范围确定为 FruitsActivity
是完全合理的。
我不确定你的 RxJava
代码是如何工作的,没有看到我猜测的代码,它与 disposed
不正确有关。
此外,即使您要创建一个作用域,它的寿命也会超过 viewModelScope
(例如:在 activity 中创建并维护一个 CoroutineScope
),我的主要问题是这种方法是 不是 intuitive/natural,人们将很难检查 CoroutineScope
维护的方式和位置
TL;DR
对于片段的一个实例,即一次又一次地 added/removed,其 ViewModel 中的 viewModelScope
仅在第一次弹出片段之前有效。此后,viewModelScope
变为非活动状态。添加回片段时如何重新初始化 viewModelScope
?或者我应该寻找更好的实现来保持它的活跃吗?
场景:
我在旧项目中使用 RxJava
到 ViewModel
从网络检索数据,并将更新发送到 UI,我使用了 LiveData。
以下是我在片段中初始化典型 ViewModel 的方式:
private val fruitsViewModel by viewModels<FruitsViewModel>()
以下是上面提到的ViewModel:
class FruitsViewModel: ViewModel {
private var category: String = FruitCategories.DEFAULT
private val _fruits = MutableLiveData<List<Fruit>>()
fun fruits(): LiveData<List<Fruit>> = _fruits
fun updateCategory(newCategory: String) {
this.category = newCategory
fetchFruits()
}
fun fetchFruits() { ... }
}
在Fragment的onViewCreated()
中,我调用了fruitsViewModel.fetchFruits()
。这很好用(没有任何明显的问题)。
今天,我用协程切换了 RxJava 特定代码,以在我的 ViewModel 中检索网络数据。像这样:
fun fetchFruits() {
/* this.fruitsJob = */
viewModelScope.launch {
...
}
}
这也很好用,即使在我替换片段并返回后也是如此。但是,如果我 保留 在 Activity 中声明的片段实例并再次使用它,viewModelScope.launch {}
将不再起作用。像这样:
class FruitsActivity: AppCompatActivity() {
private val fruitsFragment = FruitsFragment()
private fun showFruitsFragment() {
supportFragmentManager
.beginTransaction()
.replace(getFragmentContainerId(), fruitsFragment)
.addToBackStack(fruitsFragment.tag)
.commit()
}
private fun removeFragments() {
val manager = supportFragmentManager
if (manager.backStackEntryCount > 0) {
val first: FragmentManager.BackStackEntry = manager.getBackStackEntryAt(0)
manager.popBackStack(first.id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
}
}
保留片段实例的原因是只加载一次API数据,之后,它应该只在点击按钮时出现,没有任何flicker/loading。
在我调用 removeFragments()
之后,fruitsViewModel
已经调用了它的 onCleared()
用于 kept 声明 fruitsFragment
。因此,它的 viewModelScope
变为非活动状态,如果我通过调用 showFruitsFragment()
再次添加 fruitsFragment
,这将不再检索网络数据。我的 Rxjava 实现不是这种情况,因为我没有使用 viewModelScope
.
我想我可以通过将 fruitsViewModel
链接到 FruitsActivity
的实例来解决问题。但是,我想知道这是否可以被认为是最好的方法。
是的,您应该以不同的方式确定 viewModel
的范围,在您的情况下,将 fruitsViewModel
范围确定为 FruitsActivity
是完全合理的。
我不确定你的 RxJava
代码是如何工作的,没有看到我猜测的代码,它与 disposed
不正确有关。
此外,即使您要创建一个作用域,它的寿命也会超过 viewModelScope
(例如:在 activity 中创建并维护一个 CoroutineScope
),我的主要问题是这种方法是 不是 intuitive/natural,人们将很难检查 CoroutineScope
维护的方式和位置