android 为什么我不能在lambda函数下调用Kotlinsuspend函数

g6ll5ycj  于 2023-04-18  发布在  Android
关注(0)|答案(3)|浏览(144)

让我从示例代码片段开始

suspend fun executeLive(result: MutableLiveData<Person>) {

    val response = ... //suspend api request

    mediatorLiveData.removeSource(response)

    mediatorLiveData.addSource(response) {
        result.value = sortData(it) // sortData is also suspend function which sortData at Dispatcher.Default
    }

}

在这个例子中,sortData不能在lambda function下调用(在这个例子中是addSource)。而且我已经将executeLive声明为suspend,这就是为什么suspend API请求可以首先启动。但是sortData函数显示编译时错误
挂起函数只能从协程体调用
那么,如何改变代码结构来解决这个问题呢?

**更新:**是否有相关文章?

svgewumm

svgewumm1#

lambda通常是一个回调函数。回调函数之所以被称为回调函数,是因为我们在函数中 Package 了一段代码,并将其传递给其他人(或其他地方)执行。这是一个基本的控制反转,其中代码不是由你执行,而是由其他人执行(例如框架)。
例如,当你在一个按钮上设置一个onClickListener,我们不知道它什么时候会被调用,我们为框架传递一个lambda,它负责用户交互来调用指定的动作。
在你的例子中,suspend函数也没有调用sortdata,而是把它传递给mediatorLiveData对象,在它自己的上下文中调用它。你传递的lambda函数不一定要从协程体调用,因此这是不允许的。

rn0zuynd

rn0zuynd2#

您可以通过使用suspendCoroutinemediatorLiveData.addSource调用转换为挂起调用本身来解决此问题:

suspend fun executeLive(result: MutableLiveData<Person>) {

    val response = ... //suspend api request

    mediatorLiveData.removeSource(response)

    val data = suspendCoroutine<TypeOfData> { cont ->
        mediatorLiveData.addSource(response) { cont.resume(it) }
    }

    result.value = sortData(data)
}

我使用TypeOfData作为response发出的数据类型的占位符。注意,这只适用于您打算进行单次发射的情况。
如果需要跟踪多个值,可以使用callbackFlow进行实验:

suspend fun executeLive(result: MutableLiveData<Person>) {

    val response = ... //suspend api request

    mediatorLiveData.removeSource(response)

    callbackFlow<TypeOfData> {
        mediatorLiveData.addSource(response) { offer(it) }

        awaitClose { mediatorLiveData.removeSource(response) }
    }
        .collect { result.value = sortData(it) }
}
zc0qhyus

zc0qhyus3#

以下是3个工作示例:
第一:

override suspend fun findReport(date: LocalDate): ByteArray? {
        val sql = """select report from reports_table where report_date = :date"""
        val map = MapSqlParameterSource("date", date)

        return suspendCoroutine { continuation ->
            jdbcTemplate.queryForObject(sql, map) { rs, _ ->
                val param = rs.getString("report")
                CoroutineScope(Dispatchers.Default).launch {
                    continuation.resumeWith(Result.success(encryptService.decompressAndDecryptFile(param))) // decompressAndDecryptFile() is suspending function
                }
            }
        }
    }

第二:

override suspend fun findReport(date: LocalDate): ByteArray? {
        val sql = """select report from reports_table where report_date = :date"""
        val map = MapSqlParameterSource("date", date)
        val deferred = CompletableDeferred<ByteArray?>()
        jdbcTemplate.queryForObject(sql, map) { rs, _ ->
            val param = rs.getString("report")
            CoroutineScope(Dispatchers.Default).launch {
                val result =
                    encryptService.decompressAndDecryptFile(param) // decompressAndDecryptFile() is suspending function
                deferred.complete(result)
            }
        }
        return deferred.await()
    }

最后一个-使用runBlocking { }但在这种情况下,您将阻塞当前线程,直到执行协程:

override suspend fun findReport(date: LocalDate): ByteArray? {
        val sql = """select report from reports_table where report_date = :date"""
        val map = MapSqlParameterSource("date", date)
        return jdbcTemplate.query(sql, map, ResultSetExtractor { rs ->
            if (rs.next()) {
                val encodedFileEntry = rs.getString("report")
                runBlocking {
                    encryptService.decompressAndDecryptFile(encodedFileEntry)
                }
            } else {
                log.warn("There is no report for the date: $date")
                null
            }
        })
    }

相关问题