首页 > 解决方案 > 我应该独立于 Fragment 的生命周期使用 ViewModelScope 吗?

问题描述

TL;博士

对于片段的一个实例,一次又一次地添加/删除,viewModelScope在其 ViewModel 中仅在第一次弹出片段之前有效。在此之后,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'sonViewCreated()中,我打电话给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 数据,之后,它应该只在单击按钮时出现,没有任何闪烁/加载。

我打电话后removeFragments()fruitsViewModel已经把它叫作保留onCleared()声明了。因此,它变得不活动,如果我通过调用再次添加,这将不再检索网络数据。我的 Rxjava 实现不是这种情况,因为我没有使用.fruitsFragmentviewModelScopefruitsFragmentshowFruitsFragment()viewModelScope

我想我可以通过链接fruitsViewModelFruitsActivity's 实例来解决问题。但是,我想知道这是否可以被认为是最好的方法。

标签: androidandroid-fragmentskotlin-coroutinesandroid-viewmodel

解决方案


是的,您应该viewModel以不同的方式确定范围,在您的情况下确定范围fruitsViewModelFruitsActivity完全有意义的。

我不确定你的RxJava代码是如何工作的,没有看到我猜测的代码,它与它不disposed正确有关。

此外,即使您要创建一个范围,该范围比viewModelScope(例如:CoroutineScope在活动中创建和维护)我的主要问题是该方法不直观/自然,人们将更难检查CoroutineScope维护的方式和位置


推荐阅读