【问题标题】:Coroutines Flows being collected across Fragments跨片段收集的协程流
【发布时间】:2020-12-03 19:40:27
【问题描述】:

我有两个片段:GeneralInformationFrament 和 HomeFragment,我为每个片段关联了一个 ViewModel。在他们两个里面我都有一个类似的方法:

private fun getInstallationSiteInformation() {
        launch(Dispatchers.IO) {
                currentFragment.getInstallationSiteOfUser(employeeId).collect {
                    when(it.status){
                        Resource.Status.LOADING -> {
                            withContext(Dispatchers.Main){
                                //Code
                            }
                        }
                        Resource.Status.SUCCESS -> {
                            withContext(Dispatchers.Main){
                                //code
                            }
                        }
                        Resource.Status.ERROR -> {
                             withContext(Dispatchers.Main) {
                                //more code
                             }
                        }
                    }
                }

        }
}

在我拥有的每个视图模型中:

fun getInstallationSiteOfUser(employeeId: Int): Flow<Resource<InstallationSiteEntity?>> =
        installationSiteRepository.getInstallationSiteOfUser(employeeId).map{
                installationSiteResponse ->
            when(installationSiteResponse.status){
                Resource.Status.LOADING -> {
                    Resource.loading(null)
                }
                Resource.Status.SUCCESS -> {
                    val installationSite = installationSiteResponse.data
                    Resource.success(installationSite)
                }
                Resource.Status.ERROR -> {
                    Resource.error(installationSiteResponse.message!!, null)
                }
            }
        }

在 InstallationSiteRepository 我有:

fun getInstallationSiteOfUser(employeeId: Int): Flow<Resource<InstallationSiteEntity>> = flow{

        emit(Resource.loading(null))

        val installationSiteOfEmployee = employeesDao.getEmployeeDetailed(employeeId)

        remoteApiService.getInstallationSiteOfEmployee(employeeId).collect {apiResponse ->
            when(apiResponse){
                is ApiSuccessResponse -> {
                    apiResponse.body?.let {
                        installationSitesDao.insertInstallationSite(it.installationSite)}
                    emitAll(installationSitesDao.getInstallationSiteFromEmployeeId(employeeId).map { installationData ->
                        Resource.success(installationData)
                    })
                }
                is ApiErrorResponse -> {
                    emitAll(installationSitesDao.getInstallationSiteFromEmployeeId(employeeId).map { installationData ->
                        Resource.error(apiResponse.errorMessage, installationData)
                    })
                }
            }
        }

    }

在 GeneralInformationFragment 到 HomeFragment 之间的转换后不久,方法 getInstallationSiteInformation() 在 onViewCreated() 中被调用,所以我遇到的行为是流被一个接一个地收集在两个片段中,因为其中一个片段不是不再可用我得到一个 NullPointerException。我的问题是:当流源发出时,每个收集它的目标都会获得值?我描述的可能吗? GeneralInformationFragment 里面的流不应该已经取消,停止接收了吗?

[编辑 1]

在我的片段顶部有:

@AndroidEntryPoint
class GeneralInformationFragment : Fragment(), CoroutineScope {

private var job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO + job

在 Fragments 的 OnDestroy() 中:

override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }

【问题讨论】:

    标签: android kotlin-coroutines


    【解决方案1】:

    当一个流源发出时,每个收集它的目标都会获得值?

    是的。 Flows 是流。在接收者呼叫collect 之前,他们什么都不做。接收者的多次呼叫没有限制。 Flow 将为每个调用者发出其值。

    GeneralInformationFragment里面的流不应该已经取消了,停止接收了吗?

    您需要取消 Flow 收集。在getInstallationSiteInformation() 中,launch(Dispatchers.IO) {/**/} 将返回一个Job。您可以保留对 Job 的引用并在需要时取消。

    或者,您可以取消CoroutineScope

    您的代码中的CoroutineScope 不清楚,您的Fragment 是否实现了CoroutineScope

    有“开箱即用”CoroutineScopes 来代替 FragmentlifecycleScopeviewLifecycleOwner.lifecycleScope

    他们将为您处理生命周期(取消)。 lifecycleScope 绑定到 Fragment 生命周期,而 viewLifecycleOwner.lifecycleScope 绑定到 Fragment View 生命周期,onViewCreated 绑定到 onDestroyView

    我希望这至少会有所帮助。

    【讨论】:

    • 感谢您的回答。它为我澄清了很多。你认为最好的方法是什么?我这样做的方式还是使用 viewLifecycleOwner.lifecycleScope 取消协程?
    • 我将调用 launch{ //code } 替换为 viewLifecycleOwner.lifecycleScope.launch{//code} ,仅此一项就解决了我的问题。惊人的!!我不完全明白为什么,但可能是因为我缩小了发射器的可能目标。谢谢@veritas1
    猜你喜欢
    • 2020-03-29
    • 2021-11-07
    • 2020-03-07
    • 2021-01-18
    • 1970-01-01
    • 2021-10-20
    • 2015-02-11
    • 2021-11-27
    • 1970-01-01
    相关资源
    最近更新 更多