Kotlin协程通解
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)
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 | 线程池 |
Main | UI线程 |
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
可以执行切换线程操作、延期执行的流