Kotlin协程通解

XiLaiTL大约 4 分钟

Kotlin协程通解

协程在大部分的编程语言中可以分为:有栈协程(go语言的“go”+channel,利用go关键字开启并行协程,用channel进行通信)/无栈协程(async function xxx/await xxx()关键字),以及结构化并发的设计。

Kotlin的协程设计类似于结构化并发,是一种对线程池的封装。

安装依赖

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'

在mvn上找到它的最新版本Maven Repository: org.jetbrains.kotlinx » kotlinx-coroutines-core (mvnrepository.com)open in new window

def coroutines_version = '1.6.1'
runtimeOnly "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

安卓拓展

线程

Thread{
	
}.start()

协程

散装分析

runBlocking{
	//异常处理Handler
	val myExceptionHandler = CoroutineExceptionHandler {context,exception->
		println(exception.message)
	}
	//上下文工作任务Job
	val scopeJob = Job()
	//协程作用域CoroutineScope
	val scope = CoroutineScope(
		context = scopeJob + Dispatchers.Default + myExceptionHandler,
		start = CoroutineStart.DEFAULT
	)
	//协程构造器
	//工作任务job
	val job1 = scope.launch(context= ,start = ){
		//协程部分,是一个挂起函数
	}
	val job2 = scope.launch(context= Dispatchers.Main ){
		//协程部分,是一个挂起函数
		withContext(Dispatchers.IO){
			//子协程部分,是一个挂起函数
		}
		withContext(Dispatchers.IO){
			//子协程部分,是一个挂起函数
		}
		withContext(Dispatchers.IO){
			//子协程部分,是一个挂起函数
		}
		/*//等价于
		launch{
			launch{
				launch{
				
				}
			}
		}
		*/
	}
}

suspend fun 挂起函数

////定义
suspend fun xxx(){

	yield()//支持取消,在job.cancel()时返回
}

////调用
//挂起函数或者协程中
xxx()

挂起函数用于在协程中设置挂起点,让协程和主线程同时继续接下来的事情。等到挂起函数执行完毕,回到挂起点,切回调度器指定的线程处(一般切回原线程处)。

suspend关键字不提供任何作用,只是提醒函数创建者这里有个函数里面调用了个挂起函数。

真正发挥作用的是,分配了调度器的内部挂起函数。

CoroutineScope 协程作用域

CoroutineScope 是一个放置协程上下文的接口

有了作用域,就可以使用协程构造器开启一个协程

作用域构造函数

接收一个挂起函数

//必须在一个suspend函数中
coroutineScope{

}

ViewModel

依赖

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0

viewModelScope

class MyViewModel:ViewModel(){
	fun useCoroutine(){
		viewModelScope.launch(Dispatchers.IO){
			
		}
	}
}

Compose中使用协程

LaunchedEffect


@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
    LaunchedEffect(null) {
        Log.d(TAG, "这个block执行在协程${Thread.currentThread().name}中")
    }
}


//当检索词变化时,发起检索。
@Composable
fun SearchScreen() {
    ...
    var searchQuery by remember { mutableStateOf("") }
    LaunchedEffect(searchQuery) {
        // execute search and receive result
    }
    ...
}


@Composable
fun Greeting(name: String) {
    var state by remember {
        mutableStateOf(1)
    }
    var resp by remember {
        mutableStateOf("hello $name!")
    }
    LaunchedEffect(state) {
        delay(400)
        resp = "state:${state}\n这个block执行在协程${Thread.currentThread().name}中"
    }
    Column {
        Text(text = resp)
        Button(
            onClick = { ++state },
            modifier = Modifier
                .height(50.dp)
                .width(100.dp)
        ) {
            Text(text = "点一点")
        }
    }
}

使用Flow

@Composable
HomeScreen() {
  val devicesFlow = flow { emit(
    listOf(Device(1, "Device Name 1"), Device(2, "Device Name 2"))
  )}
  val devicesState = devicesFlow.collectAsState(initial = emptyList())
  // HomeScreen recomposed each time flow emit a new list of devices
  HomeScreen(devicesState.value)
}

协程构造器

阻塞性质的协程

只接收context和挂起函数

runBlocking{
	
}

占用主线程时间,顺序执行,并不并发

无返回值 launch

来自CoroutineScope的引用,接收context和start以及挂起函数

runBlocking{
	//父协程
	launch{//子协程}
}
coroutineScope{
	launch{}
}//只能用于挂起函数内
val job = CoroutineScope(EmptyCoroutineContext).launch{}

//挂起函数或者协程中
job.join()//获得上述内容的执行结果

有返回值 async

async接收context和start以及挂起函数

val slow = async(Dispatchers.Default){
	
}

//挂起函数或者协程中
slow.await()//上述内容才开始执行,并获得上述内容的执行结果

withContext

withContext(Dispatchers.Default){

}
//等价于
async(Dispatchers.Default){

}.await()

Job 工作任务

val job = CoroutineScope.launch{} 

返回值为job即协程所完成的工作任务。

job可以分为工作步骤,即suspend function的调用。

job引用用于查询状态,取消工作任务

job.cancel()
job.join()

向下传递的工作任务

val scopeJob = Job()
withContext(scopeJob + Dispatchers.IO){
	
}

Context 协程上下文

协程上下文由三部分组成:Job+Dispatcher+Handler

Job 工作任务

如上。

如果使用

val scopeJob = SupervisorJob()
val scope = CoroutioneScope(scopeJob + Dispathcers.Default + myExceptionHandler)
val job1 = scope.launch{

}
val job2 = scope.launch{
	throw
}
joinAll(job1,job2)

则即使job2被取消,job1不受影响

Dispatcher 调度器 协程派发方式

协程构造器接收context和start以及挂起函数

context可以指定调度器

CoroutineScope.launch(context=Dispatchers.DEFAULT){

}
context=
DEFAULT线程池
MainUI线程
Unconfined直接执行
IO线程池

Handler异常处理句柄

协程构造器接收context和start以及挂起函数

context可以指定异常构造器

	val myExceptionHandler = CoroutineExceptionHandler {context,exception->
		println(exception.message)
	}

Start 协程启动模式

协程构造器接收context和start以及挂起函数

其中的start定义了协程的启动模式

GlobalScope.launch(start=CoroutineStart.DEFAULT){

}
start=
DEFAULT立即调度协程,可以随时取消
LAZY只是定义,只有需要start,join,await才开始调度
ATOMIC立即调度协程,在第一个挂起点前不能被取消
UNDISPATCHED立即执行协程,在第一个挂起点时取决于调度器

通道

runBlocking{
	val channel = Channel<Int>()
	launch{
		channel.send(1)
	}
	
	launch{
		val a = channel.receive()
	}
	channel.close()
}

//级联
fun CoroutineScope.produceNumber() = produce<Int>{
	send(1)
}
fun CoroutineScope.square(n:ReceiveChannel<Int>) = produce<Int>{
	for(x in n) send(x)
}

Flow

可以执行切换线程操作、延期执行的流

上次编辑于:
贡献者: XiLaiTL