我应该独立于 Fragment 的生命周期使用 ViewModelScope 吗?

Should I use ViewModelScope independent of Fragment's lifecycle?

TL;DR

对于片段的一个实例,即一次又一次地 added/removed,其 ViewModel 中的 viewModelScope 仅在第一次弹出片段之前有效。此后,viewModelScope 变为非活动状态。添加回片段时如何重新初始化 viewModelScope?或者我应该寻找更好的实现来保持它的活跃吗?

场景:

我在旧项目中使用 RxJavaViewModel 从网络检索数据,并将更新发送到 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 维护的方式和位置