协程 使用技巧 – 回调转换
记录把回调转为协程的方法
回调转换为协程
- 回调
interface Callback<T> {
fun onSucceed(result: T)
fun onFail(e: Exception)
}
fun getResultFromCallback() {
// 用回调获取数据进行显示
mockLongTimeProcess(object: Callback<String>{
override fun onSucceed(result: String) {
Log.i(TAG, "show result=$result")
}
override fun onFail(e: Exception) {
Log.e(TAG, "e=${e.message}")
}
})
}
// 假装需要用力加载数据
fun mockLongTimeProcess(result: Callback<String>) {
thread {
Thread.sleep(3000)
if (System.currentTimeMillis() % 2 == 0L) {
result.onSucceed("Success")
} else {
result.onFail(Exception("Failure"))
}
}
}
- 改成协程
// 看起来是顺序执行,实际会挂起等待结果再执行,但不阻塞线程
fun getResultFromCoroutine() {
scope.launch {
val result = try {
getResult()
} catch (e: Exception) {
Log.e(TAG, "e=${e.message}")
""
}
Log.i(TAG, "result=$result")
}
}
// 使用 suspendCancellableCoroutine 改造回调
suspend fun getResult() = suspendCancellableCoroutine {
mockLongTimeProcess(object : Callback<String> {
override fun onSucceed(result: String) {
if (it.isCancelled) return
it.resume(result)
}
override fun onFail(e: Exception) {
it.resumeWithException(e)
}
})
}
suspendCoroutine
也能产生此效果,但无cancel()
,无法取消。而suspendCancellableCoroutine
可以取消,并抛出CancellationException
异常
回调转换为数据流
fun showResultFromFlow() {
val flow = getResultFromFlow()
launch {
// 收到数据,刷一刷
flow.collect {
Log.i(TAG, "show $it")
}
}
}
// 冷流,人来疯
fun getResultFromFlow() = callbackFlow {
// 辛苦得到了,立刻就射出去
mockLongTimeProcess {
try {
trySend(it)
} catch (e: Exception) {
close(e)
}
}
awaitClose {
// 关闭时释放资源,释放了个寂寞
}
}
// 假装需要用力加载数据流
fun mockLongTimeProcess(block: (String) -> Unit) {
thread {
for (i in 0 .. 6) {
Thread.sleep(2000)
block("Result: $i")
}
}
}