kotlin协程笔记

2024-12-08  本文已影响0人  R7_Perfect

一 挂起

suspend代表挂起,从当前线程挂起,换句话说,就是这个协程从执行他的线程上脱离
看下以下代码:

viewModelScope.launch {
    val resp = repo.uploadOss(bitmap)
    Log.d("", resp.toString())
}

  suspend inline fun uploadOss(bitmap : Bitmap) : NetResult<OssBean> {
        return withContext(Dispatchers.IO) {
            ...
        }
}

这段代码是指的往你的主线程post一个Runnable,这个Runnable就是你的协程代码:

handler.post {
    val resp = repo.uploadOss(bitmap)
    Log.d("", resp.toString())
}

执行在哪个线程

我们来看以下两段代码:

 viewModelScope.launch {
            startLoading()
            LogUtils.d("", "thread launch:${Thread.currentThread().name}" )
            val data = repo.requestHttp()
            endLoading()
}

fun requestHttp() : Response {
          LogUtils.d("", "thread suspend:${Thread.currentThread().name}" )
         val response = call()
          return response
}

打印出来:

thread launch:main @coroutine#1
thread suspend:main @coroutine#1

main: 表示这个协程的名称或入口函数是 main。
@coroutine#1: 表示这是第一个协程实例,#1 是一个标识符,用于区分不同的协程实例。
将代码稍微修改下:

fun requestHttp() : Response {
       val response = withContext(Dispatchers.IO) {
            LogUtils.d("fjb", "thread suspend:${Thread.currentThread().name}" )
            call()
        }
    return response
}

打印出来:

thread launch:main @coroutine#1
thread suspend:DefaultDispatcher-worker-1 @coroutine#1

所以,suspend并没有切换线程,真正切换线程需要withContext

挂起函数在执行完成之后,协程会重新切回它原先的线程。

协程又是如何切换回来的

它通过一个叫 Continuation 的接口的实例来返回结果:

public interface Continuation<in T> {
 public val context: CoroutineContext
 public fun resumeWith(result: Result<T>)
}

举例,以下代码:

GlobalScope.launch(Dispatchers.Main) {
 try {
 //showUser 在 await 的 Continuation 的回调函数调用后执行
 showUser(api.getUser().await())
 } catch (e: Exception) {
 showError(e)
 }

await方法定义:

public suspend fun await(): T

但在虚拟机里,他的真实签名是:

kotlinx/coroutines/Deferred.await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

以上例子代码相当于:

//注意以下不是正确的代码,仅供大家理解协程使用
GlobalScope.launch(Dispatchers.Main) {
    api.getUser().await(object: Continuation<User>{
 override fun resume(value: User) {
 showUser(value)
 }
 override fun resumeWithException(exception: Throwable){
 showError(exception)
 }
 })
}

await相当于如下实现:

fun await(continuation: Continuation<User>): Any {
 try {
 val user = ...
 handler.post{ continuation.resume(user) }
 } catch(e: Exception) {
 handler.post{ continuation.resumeWithException(e) }
 }
}

二。使用时机

协程中有luanch和async两个函数
我们来看下以下两种获取网络结果的方式:

第一种

viewModelScope.launch {
    startLoading()
    val resp = repo.uploadOss(bitmap)
    Log.d("", resp.toString())
    endLoading
}

第二种

viewModelScope.launch {
    startLoading()
    val def: Deferred<NetResult<OssBean>> = async {
        repo.uploadOss(bitmap)
    }
    Log.d("", def.await().toString())
  endLoading
}

第一种:
调用 repo.uploadOss(bitmap) 时,协程会挂起,直到该函数完成并返回结果。然后,结果会被赋值给resp变量,并打印到日志中。
第二种:
async 会启动一个新的协程并返回一个 Deferred 对象。Deferred对象代表一个将来会返回结果的任务。通过调用 def.await(),当前协程会挂起,直到 async 块中的任务完成并返回结果。然后,结果会被打印到日志中。

结论:
在大多数情况下,两种方式的效果是相同的,但 async 方式更适合需要并行执行多个异步任务的场景,因为它可以启动多个并行任务并等待它们的结果。
比如:

viewModelScope.launch {
    startLoading()
    val def: Deferred<NetResult<OssBean>> = async {
        repo.uploadOss(bitmap)
    }
   val count: Deferred<NetResult<Int>> = async {
        repo.countOss()
    }
    Log.d("", "上传结果${def.await().toString()}, 现在一共有${count.await().toString()}")
  endLoading
}
上一篇 下一篇

猜你喜欢

热点阅读