学习笔记-kotlin协程

214 阅读3分钟

kotlin协程在Android 被用来代替线程,可以当做线程框架来使用,主要用于处理异步任务,并发等。
线程是依靠操作系统调度实现的线程切换,协程是编程语言层面的,轻量级的线程。

协程允许我们在单线程模式下模拟多线程的效果

协程创建:



//会阻塞主线程
runBlocking {
    /* ... */ 
}

//全局的单例对象
GlobalScope.launch {
     /* ... */ 
}


/* Android推荐方式
 * 使用coroutineContext 
 * 这样在activity 里就可以使用 job来实现对协程的统一处理 栗如 :job.cancel() 等
 *
 */

var job = Job()
val coroutineScope = CoroutineScope(job)
coroutineScope.launch {
     /* ... */ 
}

// viewmodel 里可以直接使用 viewModelScope 
viewModelScope.launch{
    /* ... */
}

coroutineScope

coroutineScope是一个挂起函数,在协程内部或挂起函数使用,创建一个协程的作用域 作用域内的所有子协程执行完成之前,会阻塞当前协程。

 GlobalScope.launch {
            Log.e("Scope", "GlobalScope")
            coroutineScope {
                launch {
                    delay(2000)
                    Log.e("Scope", "coroutineScope1")
                }
                launch {
                    delay(1000)
                    Log.e("Scope", "coroutineScope2")
                }
            }
            Log.e("Scope", "GlobalScope2")
        }

执行顺序为:

Scope: GlobalScope
Scope: coroutineScope2
Scope: coroutineScope1
Scope: GlobalScope2

取消作用域:

  GlobalScope.launch {
            Log.e("Scope", "GlobalScope")
                launch {
                    delay(2000)
                    Log.e("Scope", "coroutineScope1")
                }
                launch {
                    delay(1000)
                    Log.e("Scope", "coroutineScope2")
                }
            Log.e("Scope", "GlobalScope2")
        }

执行顺序为:

Scope: GlobalScope
Scope: GlobalScope2
Scope: coroutineScope2
Scope: coroutineScope1

同一个作用域下的所有子协程是并发运行的,外部协程取消之后,所有子协程都会停止运行

suspendcoroutine

suspendcoroutine挂起函数必须在协程作用域或者挂起函数调用,会立即挂起协程,它传入一个lambda 表达式,提供了 一个 continuation 参数,调用它的 resume() 或者 resumeWithException () 可以恢复协程。
resume(t : T) 需要传入一个参数,并将参数作为 suspendcoroutine{} 函数的返回值, resumeWithException (e : Throwable) 则会抛出异常, try catch 捕获处理即可

 GlobalScope.launch {
            try {
                var str = test()
            } catch (e: Exception) {
                Log.e("Scope", "${e.message}")
            }
        }
        
 suspend fun test() = suspendCoroutine<String> {
        if (true){
            it.resume("success")
        } else {
            it.resumeWithException(Exception())
        }
    }

协程取值

async {} 函数在协程作用域内部使用,函数会立即执行,并返回 Deferred 类型,调用 Deferredawait() 方法即可得到函数最后一行的值。 调用 await() 方法时,若函数内的代码没执行完,会阻塞当前协程。

GlobalScope.launch {
    var a = async { "string" }.await()
}
a == "string"

withContext(Dispatchers){} 函数在协程作用域或者挂起函数中使用,并阻塞当前协程,返回最后一行的值。 传入一个 Dispatchers 值。

GlobalScope.launch {
    var a = withContext(Dispatchers.IO){ "string" }
}
a == "string"

Dispatchers

Dispatchers.default 低并发的线程策略,适合计算密集型任务
Dispatchers.io 高并发 适合阻塞等待多的任务
Dispatchers.main Android主线程

coroutineScope 以外的挂起函数都可以传入线程参数,withContext 为必传

总结

Globalscope.launch{}runBlocking {}CoroutineScope(job) 可以在任意地方构建协程作用域。
coroutinescope{} 函数在协程作用域和挂起函数构建一个作用域。
launch {} 在协程作用域内部调用,开启一个子协程。

async {}withContext(Dispatchers){} 可以并获取返回值 , async{}.await() 阻塞协程,后者直接阻塞

同一个作用域下的所有子协程是并发运行的,并且生命周期跟随外部协程。

delay
delay 是一个非阻塞式的挂起函数,可以在协程作用域和挂起函数调用