深入理解Kotlin协程如何将回调改写成挂起函数
Posted 川峰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Kotlin协程如何将回调改写成挂起函数相关的知识,希望对你有一定的参考价值。
最简单的复写挂起函数的回调:
suspend fun suspendFunc() = suspendCoroutine<Int>
it.resumeWith(Result.success(1))
只不过真正的挂起需要真正的切换线程,如果直接调用的话相当于没有挂起。
suspend fun getUserSuspend(name: String) = suspendCoroutine<User> continuation ->
githubApi.getUserCallback(name).enqueue(object: Callback<User>
override fun onFailure(call: Call<User>, t: Throwable) =
continuation.resumeWithException(t)
override fun onResponse(call: Call<User>, response: Response<User>) =
response.takeIf it.isSuccessful ?.body()?.let(continuation::resume)
?: continuation.resumeWithException(HttpException(response))
)
suspend fun main()
val user = getUserSuspend("bennyhuo")
showUser(user)
支持取消的回调转协程的完整写法:
import com.bennyhuo.kotlin.coroutines.advanced.common.gitHubServiceApi
import kotlinx.coroutines.suspendCancellableCoroutine
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine //可取消
continuation ->
continuation.invokeOnCancellation
cancel() //调用retrofit的取消方法
enqueue(object: Callback<T>
override fun onFailure(call: Call<T>, t: Throwable)
continuation.resumeWithException(t)
override fun onResponse(call: Call<T>, response: Response<T>)
response.takeIf it.isSuccessful ?.body()?.also continuation.resume(it)
?: continuation.resumeWithException(HttpException(response))
)
suspend fun main()
val user = gitHubServiceApi.getUserCallback("flycumt").await()
println(user)
当然实际开发中不用自己写,Retrofit的api中本身有实现await()方法,awaitResponse()方法等。
CompletableFuture 添加回调的写法:
import com.bennyhuo.kotlin.coroutines.advanced.utils.log
import kotlinx.coroutines.suspendCancellableCoroutine
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
suspend fun main()
val result = CompletableFuture.supplyAsync
3
.await()
log(result)
suspend fun <T> CompletableFuture<T>.await(): T
if(isDone)
try
return get()
catch (e: ExecutionException)
throw e.cause ?: e
return suspendCancellableCoroutine //可取消
cancellableContinuation ->
cancellableContinuation.invokeOnCancellation
cancel(true) //取消
whenComplete value, throwable ->
if(throwable == null)
cancellableContinuation.resume(value)
else
cancellableContinuation.resumeWithException(throwable.cause ?: throwable)
CompletableFuture本身也有实现await()方法。
模仿给Handler扩展添加可取消的支持:
suspend fun <T> Handler.run(block: () -> T) = suspendCoroutine<T> continuation ->
post
try
continuation.resume(block())
catch (e: Exception)
continuation.resumeWithException(e)
suspend fun <T> Handler.runDelay(delay: Long, block: () -> T) = suspendCancellableCoroutine<T> continuation ->
val message = Message.obtain(this) //Message obtain(Handler h, Runnable callback)
try
continuation.resume(block())
catch (e: Exception)
continuation.resumeWithException(e)
.also
it.obj = continuation //message.obj
continuation.invokeOnCancellation
removeCallbacksAndMessages(continuation) //通过Handler的removeCallbacksAndMessages方法来取消回调, 参数就是前面设置的message.obj的值
sendMessageDelayed(message, delay)
suspend fun main()
Looper.prepareMainLooper()
GlobalScope.launch
val handler = Handler(Looper.getMainLooper())
val result = handler.run "Hello"
val delayedResult = handler.runDelay(5000) "World"
log(result, delayedResult)
Looper.getMainLooper().quit()
Looper.loop()
这个例子的主要意图是,Hanlder可以通过定义扩展函数的方式来延时获取一些东西,比如Activity刚创建的时候,拿不到view的宽和高,就可以使用这种方法。
上面三个例子主要是针对可取消的写法,如果实际用,不用自己写,直接导库就行。
参考:[Kotlin 协程] 常规操作:怎么把回调转成挂起函数?
以上是关于深入理解Kotlin协程如何将回调改写成挂起函数的主要内容,如果未能解决你的问题,请参考以下文章
深入理解Kotlin协程协程作用域启动模式调度器异常和取消使用篇