Kotlin 协程基础Coroutine

Kotlin 协程基础Coroutine [kəruː’tiːn]

需要了解的概念,类

1.协程与线程的区别

本质上,协程是轻量级的线程

一个线程中可以有N个协程。协程中也可以有N个子协程。

2.Dispatchers类 -调度器,指定协程运行在哪个线程中

@JvmStatic //与IO共享线程池,区别在于Default限制了最大并发数,最少2个,最大为cpu的核数
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()

@JvmStatic//UI线程
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher

@JvmStatic//未定义的线程,使用这个启动的协程会立即在当前的线程执行
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined

@JvmStatic//一个用于经常IO操作的线程池,告并行量。与Default共享线程池
public val IO: CoroutineDispatcher = DefaultScheduler.IO

3.关于Job类

协程启动后会返回一个job对象,通过此对象,可以手动控制协程的取消等操作。

cancel//取消
join//挂起,等待协程任务执行结束
cancelAndJoin() // 取消该任务并等待它结束

4.挂起函数

suspend 关键字修饰函数,只能运行在协程中。程序运行到该函数时,会被挂起直到该函数执行完成才会继续执行

构造方式

源码:

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
CoroutineStart 的状态

DEFAULT//默认,立即执行,可取消

LAZY //需要手动控制start 时机

ATOMIC //This is similar to [DEFAULT], but the coroutine cannot be cancelled before it starts executing. --协程开始执行前不能取消
 
UNDISPATCHED// 立即在当前线程执行

1.GlobalScope.launch(),启动一个top-level级协程,在Android中,跟随应用的生命周期。如非必要,尽量不要使用。

fun main() {
   GlobalScope.launch(Dispathcer.Main) {
     //.....
   }
    GlobalScope.launch { // 启动一个新的协程并继续
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Hello,") // 协程已在等待时主线程还在继续
   job.join() //等待直到子协程执行结束 
}


2.runBlocking ,在被runBlocking修饰的函数中,只有子协程全部执行结束,函数才会结束。

fun main() = runBlocking<Unit> { // 开始执行主协程
    var job = GlobalScope.launch { // 在后台启动一个新的协程并继续
        delay(1000L)
        println("World!")
    }
    println("Hello,") // 主协程在这里会立即执行
    job.join() // 等待直到子协程执行结束 
}

3.作用域构建器

除了由不同的构建器提供协程作用域之外,还可以使用 coroutineScope 构建器声明自己的作用域。它会创建一个协程作用域并且在所有已启动子协程执行完毕之前不会结束。

fun main() = runBlocking { // this: CoroutineScope
  
    launch { //内嵌launch
        delay(200L)
        println("Task from runBlocking")
    }
    
  
  
    coroutineScope { // 创建一个协程作用域
        launch {
            delay(500L) 
            println("Task from next launch")
        }
        delay(100L)
        println("Task from coroutine scope") // 这一行会在内嵌 launch 之前输出
    }
    
  
    println("Coroutine scope is over") // 这一行在所有协程执行完毕后才输出
}
Task from coroutine scope
Task from runBlocking
Task from next launch
Coroutine scope is over

runBlockingcoroutineScope 主要区别在于,runBlocking 方法会阻塞当前线程来等待, 而 coroutineScope 只是挂起,会释放底层线程用于其他用途。 由于存在这点差异,runBlocking 是常规函数,而 coroutineScope 是挂起函数。

4.CoroutineScope(Dispatchers.Main + viewModelJob).launch

 	class MainViewModel(application: Application) : AndroidViewModel(application) {
   
    /**
     * 这是此 ViewModel 运行的所有协程所用的任务。
     * 终止这个任务将会终止此 ViewModel 开始的所有协程。
     */
    private val viewModelJob = SupervisorJob()

    /**
     * 这是 MainViewModel 启动的所有协程的主作用域。
     * 因为我们传入了 viewModelJob,可以通过调用viewModelJob.cancel() 
     * 来取消所有 uiScope 启动的协程。
     */
    private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
   
     fun <T> launchUI() {
        uiScope.launch() {
           //.......省略代码
        }

    }
    
    override fun onCleared() {
        super.onCleared()
        viewModelJob.cancel()
    }
}

AndroidX lifecycle v2.1.0 开始在 ViewModel 类中引入了扩展属性 viewModelScope。可以直接viewModelScope.launch 启动协程,不需要手动cancle。

4.withContext(Dispatcher.xxx),必须在协程体内执行,,带返回结果, 同步关系

private fun launchFromContext() {
        CoroutineScope.launch {
            val time1 = System.currentTimeMillis()
            var task1 = withContext(Dispatchers.IO) {
                delay(200)
                Log.i("kotlin", "io== ${Thread.currentThread().name}")
                1
            }
            var task2 = withContext(Dispatchers.Main) {
                delay(100)
                Log.i("kotlin", "Main== ${Thread.currentThread().name}")
                2
            }
            Log.i("kotlin", "launchFromContext end + $task1 + $task2  time==${System.currentTimeMillis() - time1}"
            )
        }
    }
launchFromContext end
io== DefaultDispatcher-worker-1
Main== main
launchFromContext end + 1 + 2  time==325

task1执行完后,再执行task2,task2执行完之后,才会执行最终的log

5.async 异步关系

  private fun launchFromAysc() {
        CoroutineScope(Dispatchers.Main).launch {
            val time1 = System.currentTimeMillis()
            val task1 = async(Dispatchers.IO) {
                Log.e("kotlin", "1.task1 start")
                delay(5000)
                Log.e("kotlin", "1.执行task1.... [当前线程为:${Thread.currentThread().name}]")
                "one"  //返回结果赋值给task1
            }

            val task2 = async(Dispatchers.IO) {
                Log.e("kotlin", "2.task2 start")
                delay(3000)
                Log.e("kotlin", "2.执行task2.... [当前线程为:${Thread.currentThread().name}]")
                "two"  //返回结果赋值给task2
            }
            Log.e("kotlin", "task1 = ${task1.await()}  , task2 = ${task2.await()} , 耗时 ${System.currentTimeMillis() - time1} ms  [当前线程为:${Thread.currentThread().name}]")
          
           Log.i("kotlin","launchFromAysc end")
        }
    }
1.task1 start]
2.task2 start]
2.执行task2.... [当前线程为:DefaultDispatcher-worker-1]
1.执行task1.... [当前线程为:DefaultDispatcher-worker-1]
task1 = one  , task2 = two , 耗时 5041 ms  [当前线程为:main]

,await是挂起函数,执行到task1.await()时,挂起,接着执行task2.await(),等task1和task2都有数据返回时,才执行最终的打印。

async 就类似于 launch。它启动了一个单独的协程,与其他协程并发工作。不同之处在于 launch 返回一个 Job 并且不附带任何结果值,而 async 返回一个 Deferred —— 一个轻量级的非阻塞 future

Android ViewModel中协程的应用

class MainViewModel(application: Application) : AndroidViewModel(application) {
    private val viewModelJob = SupervisorJob()
    private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
    public var data: MutableLiveData<List<Data>> = MutableLiveData()

    fun <T> launchUI(
        block: suspend CoroutineScope.() -> Response<T>,
        onError: (e: Throwable) -> Unit = {},
        onSuccess: (data: T) -> Unit = {},
        onComplete: () -> Unit = {}
    ) {
        uiScope.launch(CoroutineExceptionHandler { _, throwable -> onError(throwable) }) {
            var result = block()
            if (result.errorCode == 0) {
                onSuccess(result.data)
            } else {
                onError(Exception("返回错误码异常" + result.errorCode))
            }
            onComplete()
        }
    }

    fun test() {
        launchUI({
            Log.i("kotlin", "start" + Thread.currentThread().name)
            RetrofitUtils.getService(RequestService::class.java).getDatas().await()
        }, onSuccess = {
            Log.i("kotlin", "onSuccess" + Thread.currentThread().name)
            data.value = it
        }, onError = {
            Log.i("kotlin", "onError" + it.message)
        }, onComplete = {
            Log.i("kotlin", "onComplete" + Thread.currentThread().name)
        })
    }


    override fun onCleared() {
        super.onCleared()
        viewModelJob.cancel()
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin协程是一种轻量级的并发框架,它可以在不创建新线程的情况下实现异步操作。Kotlin协程的实现原理是基于挂起函数和Continuation(协程上下文)。 在Kotlin协程中,挂起函数是指可以被暂停执行,并在稍后继续执行的函数。在挂起函数中,可以使用`suspend`关键字来标记该函数为挂起函数。当调用一个挂起函数时,该函数会返回一个`Continuation`对象,该对象可以被用来在稍后的时间点恢复挂起函数的执行。 Kotlin协程的调度器会在适当的时候调用Continuation对象的`resume`方法来恢复挂起函数的执行。当一个挂起函数被恢复执行时,它会从上一次挂起的地方继续执行,直到函数结束或者再次遇到挂起点。 Kotlin协程的实现原理可以用以下伪代码来说明: ```kotlin fun main() { launch { println("Hello") delay(1000) println("World") } } suspend fun delay(time: Long) { // 挂起当前协程,等待一段时间 // 通过Continuation对象来恢复协程的执行 suspendCoroutine<Unit> { continuation -> Timer().schedule(time) { continuation.resume(Unit) } } } fun launch(block: suspend () -> Unit) { // 创建一个新的协程,并将其加入到调度器中 val coroutine = Coroutine(block) coroutine.start() } class Coroutine(private val block: suspend () -> Unit) { fun start() { block.startCoroutine(this) } } class Continuation(private val coroutine: Coroutine) { fun resume(value: Any?) { coroutine.resume(value) } } suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T { // 挂起当前协程,等待Continuation对象被调用 // 通过Continuation对象来恢复协程的执行 return suspendCoroutineOrReturn { continuation -> block(Continuation(coroutine)) } } ``` 在上面的代码中,`launch`函数用于创建一个新的协程,并将其加入到调度器中。`Coroutine`类表示一个协程,`start`方法用于启动协程的执行。`suspendCoroutine`函数用于挂起当前协程,并等待Continuation对象被调用以恢复协程的执行。`delay`函数使用`suspendCoroutine`函数来实现挂起功能,并在一定时间后恢复协程的执行。 Kotlin协程的实现原理比较复杂,但是开发者只需要关注如何使用协程来实现异步操作即可。通过使用协程,开发者可以编写出更加简洁、易于理解、易于维护的异步代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值