协程是一种“轻量级线程“,从Kotlin 1.1开始引入。由于一些耗时操作(如网络IO、文件 IO、CPU或GPU密集型任务)会使线程阻塞直到操作完成,协程提供了一种避免线程阻塞、开销更小且更加可控的异步操作。
协程完全通过编译技术实现(不需要来自 VM 或 OS 端的支持),挂起通过代码来生效。基本上,每个挂起函数都转换为状态机,其中的状态对应于挂起调用。刚好在挂起前,下一状态与相关局部变量等一起存储在编译器生成的类的字段中。在恢复该协程时,恢复局部变量并且状态机从刚好挂起之后的状态进行。
协程可以被挂起而不阻塞线程,线程的阻塞代价是昂贵的。尤其是创建线程数量是有限的,当线程被阻塞,那些重要的任务就有可能被延迟。协程的挂起几乎是没有代价的,它不需要切换上下文。协程的本质是最大限度的利用线程所获得的cpu时间,提高线程的效率。
使用launch函数
public fun launch(
context: CoroutineContext = DefaultDispatcher,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit): Job {}
第一个参数为协程的上下文
第二个参数为协程的启动选项
第三个参数为一个suspend修饰的函数
fun test(){
launch(Unconfined) {
delay(1000)
print("I am a Coroutine")
}
print("I am mainThread")
}
launch()函数开启了一个协程,由于协程被挂起,所以协程之外的代码先被执行,又因为协程所处的上下文已经销毁,所以不能完整执行结束。
注意:suspend函数只能用于启动协程的函数(闭包)中
launch函数返回一个Job借口对象,可以对协程进行管理。
Job接口的常用方法有
Job的几种状态
协程的生命周期
延迟一个协程
fun test(){
val task = launch(Unconfined,CoroutineStart.LAZY) {
println("I am a Coroutine")
}
println("I am mainThread")
task.start()
}
finally中的协程代码
fun test(){
val task = launch(Unconfined) {
try{
delay(1000)
println("try at coroutine")
}finally {
println("finally at coroutine")
}
}
task.cancel()
Thread.sleep(2000)
println("I am mainThread")
}
协程取消时,finally语句依然会被执行。但是当协程所在线程已经停止,finally也会停止,不会继续执行。可以使用NonCancellable让finallly完整执行。
fun test(){
val task = launch(Unconfined) {
try{
println("try at coroutine")
}finally {
run(NonCancellable) {
delay(2000)
println("finally at coroutine")
}
}
}
println("I am mainThread")
task.cancel()
}
协程的超时时间
fun test(){
launch(Unconfined) {
withTimeout(1000){
try{
println("try at coroutine")
}finally {
delay(2000)
println("finally at coroutine")
}
}
}
println("I am mainThread")
Thread.sleep(2000)
}
fun test(){
val task = launch(Unconfined) {
var i=0
while(true){
i++
println(i)
delay(200)
}
}
Thread.sleep(1000)
if(task.isActive) task.cancel()
Thread.sleep(1000)
println("I am mainThread")
}
使用async函数开启异步协程
将launch函数换成async即可启动异步协程
fun test(){
async {
repeat(10){
delay(200)
println("task1")
}
}
async {
repeat(10){
delay(200)
println("task2")
}
}
Thread.sleep(2000)
println("I am mainThread")
}
runBlocking用于main函数或test函数,用于实现类似主协程的效果
fun main(args: Array) = runBlocking {
launch {
println("before delay")
delay(1000)
println("after delay")
}
delay(2000)
//Thread.sleep(2000)
println("I am mainThread")
}
协程的优点主要是①数量多②非阻塞③管理方便④异步并发时同步简单。
所以可以使用launch(async)+async+async来启动两个异步任务并直接在launch更新UI,无需使用handler机制;多线程之间同步也不需要写过多的代码。
异步协程launch+async+async
fun testAsync(){
//开启两个异步任务;这里只能用async,因为只有async有await()获取结果,并且异步
val task1 = async {
repeat(100){
Log.d("Task1","当前线程:${Thread.currentThread().name}")
}
"AsyncTask1"
}
val task2 = async {
repeat(100){
Log.d("Task2","当前线程:${Thread.currentThread().name}")
}
"AsyncTask2"
}
//更新UI或async
launch(Unconfined) {
Log.d("UI1","当前线程:${Thread.currentThread().name}")
//当前UI线程的协程阻塞,但是不会使UI阻塞
text1.text = task1.await()
text2.text = task2.await()
Log.d("UI2","当前线程:${Thread.currentThread().name}")
}
}
同步协程launch+async+async
fun testSync(){
launch(Unconfined) {
Log.d("UI1","当前线程:${Thread.currentThread().name}")
val res1 = async {
repeat(100){
Log.d("Task1","当前线程:${Thread.currentThread().name}")
}
"AsyncTask1"
}.await() //挂起
val res2 = async {
repeat(100){
Log.d("Task2","当前线程:${Thread.currentThread().name}")
}
"AsyncTask2"
}.await()
Log.d("UI2","当前线程:${Thread.currentThread().name}")
text1.text = res1
text2.text = res2
}
}
以上,我们可以使用launch或者async+几个async来代替以前的多线程。而当我们只需要让几个任务串行而不需要返回值,可以只用launch;当我们需要几个异步或者需要获得返回值,就用async,没有特殊需求就可自由选用。