android - suspendCancellableCoroutine 返回 CompletedWithCancellation 而不是实际类型
问题描述
kotlinx-coroutines-core
当我将依赖项从 1.3.2 更新到 1.3.3时,我遇到了一个奇怪的问题。但是,下面的独立示例也重现了 1.3.2 的问题。
我有一个基于回调的操作队列的扩展方法。此扩展方法用于suspendCancellableCoroutine
包装回调用法并将其转换为挂起函数。现在,一切都可以正常工作,但是从挂起函数返回的结果对象不是类型T
,而是CompletedWithCancellation<T>
,它是协程库的私有类。
奇怪的是,如果我c.resume("Foobar" as T, {})
在内部调用suspendCancellableCoroutine
,它工作得很好。使用回调例程时,值是 aString
在传递到 to 之前c.resume()
,但它被包装在一个CompletedWithCancellation
对象中。
这是重现该问题的代码:
@ExperimentalCoroutinesApi
class MainActivity : AppCompatActivity() {
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Timber.plant(Timber.DebugTree())
setContentView(R.layout.activity_main)
val vm = ViewModelProviders.of(this)
.get(MainViewModel::class.java)
vm.liveData.observe(this, Observer {
findViewById<TextView>(R.id.mainText).text = "Got result: $it"
})
vm.getFoo()
}
}
@ExperimentalCoroutinesApi
class MainViewModel : ViewModel() {
private val manager = OperationManager()
val liveData = MutableLiveData<String>()
fun getFoo() {
viewModelScope.launch {
val op = Operation(manager, "Foobar")
val rawResult = op.get<Any>()
Timber.d("Raw result: $rawResult")
val op2 = Operation(manager, "Foobar")
val result = op2.get<String>()
Timber.d("Casted result: $result")
liveData.postValue(result)
}
}
}
class OperationManager {
private val operationQueue = ConcurrentLinkedQueue<Operation>()
private val handler = Handler(Looper.getMainLooper())
private val operationRunnable = Runnable { startOperations() }
private fun startOperations() {
val iter = operationQueue.iterator()
while (iter.hasNext()) {
val operation = iter.next()
operationQueue.remove(operation)
Timber.d("Executing operation $operation")
operation.onSuccess(operation.response)
}
}
fun run(operation: Operation) {
addToQueue(operation)
startDelayed()
}
private fun addToQueue(operation: Operation) {
operationQueue.add(operation)
}
private fun startDelayed() {
handler.removeCallbacks(operationRunnable)
handler.post(operationRunnable)
}
}
open class Operation(private val manager: OperationManager, val response: Any) {
private val listeners = mutableListOf<OperationListener>()
fun addListener(listener: OperationListener) {
listeners.add(listener)
}
fun execute() = manager.run(this)
fun onSuccess(data: Any) = listeners.forEach { it.onResult(data) }
}
@ExperimentalCoroutinesApi
suspend fun <T> Operation.get(): T = suspendCancellableCoroutine { c ->
@Suppress("UNCHECKED_CAST")
val callback = object : OperationListener {
override fun onResult(result: Any) {
Timber.d("get().onResult() -> $result")
c.resume(result as T, {})
}
}
addListener(callback)
execute()
}
interface OperationListener {
fun onResult(result: Any)
}
请注意,在调用之前c.resume()
,它的类型应该是result
。String
但是,一旦挂起功能完成,它就不会String
出现。getFoo()
这是什么原因造成的?
解决方案
解决方案是:
c.resume(result as T)
代替:
c.resume(result as T, {})
似乎前者处理了resume()
正确getResult()
调用 after 的执行,而后者仅在resume()
调用 before时才有效getResult()
。
推荐阅读
- javascript - API 耗时过长,地图函数在数据加载之前触发
- powerbi - 如何让 exceptAll 忽略切片器?
- xml - 在 XML 中传递多个参数
- logback - Dropwizard 日志删除不适用于 archivedFileCount 集
- excel - 从一张纸的一列到另一张纸的多列中查找值
- applescript - 当我使用脚本编辑器打开 iMovie 时,我发现这些垃圾是什么?
- c# - Func委托中的out参数修饰符(C#)
- javascript - Reducer 从数组返回 NAN
- mysql - MySQL - 在多列中排序特定字段值
- kendo-ui - 剑道UI提示只有1个按钮