【问题标题】:What would be the cases if Coroutine's Deferred type gets null?如果 Coroutine 的 Deferred 类型为 null 会怎样?
【发布时间】:2020-10-15 21:08:37
【问题描述】:

我在我的代码中使用嵌套的协程块。当我尝试将延迟类型的结果传递给变量时,我得到了一个空值。因此,它会导致转换问题,即 kotlin.TypeCastException: null cannot be cast to non-null type kotlin.collections.ArrayList in getNearbyHealthInstitutions() 方法的返回行。我相信,我在某个时候做了正确的实现,但是我缺少什么来从 Deferred 的结果中获取空值?有趣的是,当我调试它时,它确实返回了预期值。我认为这应该是并发问题,或者我不知道为什么它首先在调试模式下工作。有什么想法吗?

// Invocation point where resides in a callback
GlobalScope.launch(Dispatchers.Main) {
    nearbyHealthInstitutionSites.value = getNearbyHealthInstitutions()
}

private suspend fun getNearbyHealthInstitutions(radius: Meter = DEFAULT_KM_RADIUS) : ArrayList<Hospital> {
    return CoroutineScope(Dispatchers.IO).async {
        val list = getHealthInstitutions()
        val filteredList = list?.filter { it.city == state?.toUpperCase() } as MutableList<Hospital>
        Log.i(MTAG, "nearby list is $filteredList")
        Log.i(MTAG, "nearby list's size is ${filteredList.size}")

        var deferred: Deferred<MutableList<Hospital>>? = null

        addAllNearbyLocations(onEnd = { nearbyHealthInstitutions ->
            deferred = async {
                findNearbyOfficialHealthInstitutions(
                    officialHealthInstitutionList = filteredList as ArrayList<Hospital>,
                    nearbyHealthInstitutions = nearbyHealthInstitutions
                )
            }
        })

        val result = deferred?.await()

        return@async result as ArrayList<Hospital>
    }.await()
}

private suspend fun findNearbyOfficialHealthInstitutions(officialHealthInstitutionList: ArrayList<Hospital>, nearbyHealthInstitutions: MutableList<Hospital>): MutableList<Hospital> {
        return GlobalScope.async(Dispatchers.Default) {
            val result = mutableListOf<Hospital>()

            officialHealthInstitutionList.forEach {
                nearbyHealthInstitutions.forEach { hospital ->
                    StringSimilarity.printSimilarity(it.name, hospital.name)

                    val similarity = StringSimilarity.similarity(it.name, hospital.name.toUpperCase())

                    if (similarity > SIMILARITY_THRESHOLD) {
                        Log.i(MTAG, "findNearbyOfficialHealthInstitutions() - ${it.name} and ${hospital.name.toUpperCase()} have %$similarity")
                        result.add(hospital)
                    }

                }
            }

            Log.i(TAG, "------------------------------------------")
            result.forEach {
                Log.i(MTAG, "findNearbyOfficialHealthInstitutions() - hospital.name is ${it.name}")
            }

            return@async result
        }.await()
    }

【问题讨论】:

  • 为什么不检查它是否为空并返回一个空列表呢?
  • addAllNearbyLocations异步吗?
  • @m0skit0 它应该返回非空对象。检查空能力不是我在这里遇到的问题。真正的问题是,它首先不应该返回空值。
  • @Tenfour04 是正确的,它是异步的。要桥接使用回调和协程的旧异步代码,请检查suspendCoroutine。我会写一个全面的答案。
  • 当某个库或 API 中的函数采用回调参数时,这是因为它在后台线程上运行一些代码,然后在完成时调用您的回调。这是异步的定义。因此,当您到达deferred?.await() 行时,尚未调用回调。正如@m0skit0 的回答所示,您可以使用suspendCoroutine 将非协程异步库代码转换为挂起函数。

标签: android kotlin asynchronous coroutine kotlin-coroutines


【解决方案1】:

由于addAllNearbyLocations() 是异步的,您的协程需要等待回调被调用才能继续执行。您可以为此使用suspendCoroutine API。

val result = suspendCoroutine { continuation ->
    addAllNearbyLocations(onEnd = { nearbyHealthInstitutions ->
          findNearbyOfficialHealthInstitutions(
                officialHealthInstitutionList = filteredList as ArrayList<Hospital>,
                nearbyHealthInstitutions = nearbyHealthInstitutions
          ).let { found -> continuation.resume(found) }
    })
}

另外,您应该使用List 而不是ArrayListMutableList,您应该始终使用通用接口而不是该接口的特定实现。这也消除了一些强制转换(理想情况下,这段代码中应该没有强制转换)。

【讨论】:

  • 删除异步块时收到Suspension functions can be called only within coroutine body 警告。我为什么要删除块?顺便说一句,findNearbyOfficialHealthInstitutions 是暂停方法。
  • 另外,addAllNearbyLocations 返回 Unit,所以我不能在 let 范围内使用它的结果。
  • 使调用findNearbyOfficialHealthInstitutions 的lambda 跨行。 let 应用于findNearbyOfficialHealthInstitutions 结果,而不是addAllNearbyLocations。您不需要异步,因为 findNearbyOfficialHealthInstitutions 已经是异步的。您实际上是在用另一个 async/await 包装 async/await。如果你愿意,你可以保留它,但它只是多余的,只会使代码更难阅读。
猜你喜欢
  • 2019-06-22
  • 2016-03-25
  • 1970-01-01
  • 2017-07-07
  • 2011-04-27
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 2011-10-21
相关资源
最近更新 更多