Kotlin Coroutine初探(一)

Kotlin Coroutine初探(一)

2017年,Google 宣布Kotlin 成为 Android 的官方开发语言,并在1.1版本加入了对 Coroutine(协程,可以简单看作是轻量级线程)的支持。

Coroutine是一种并发设计模式,在 Android 平台上使用它可以简化异步执行的代码。下面就让我们一起使用Coroutine进行编码吧!

启动协程

协程任务的构建需要通过Coroutine Builder来实现。

    /**
     * @param context 默认所创建的 Coroutine 会自动继承当前 Coroutine 的 context,如果有额外的 context 需要传递给所创建的 Coroutine 则可以通过第一个参数来设置
     * @param start 配置协程的启动模式,包含 DEFAULT、LAZY、ATOMIC和UNDISPATCHED四种模式
     * @param block 需要执行的方法
     *
     * @return 返回生成的协程任务对象Job
     * */
    fun CoroutineScope.launch(
            context: CoroutineContext = EmptyCoroutineContext,
            start: CoroutineStart = CoroutineStart.DEFAULT,
            block: suspend CoroutineScope.() -> Unit
    ): Job 

默认的Dispatchers

构建协程时,使用Dispatchers中定义的类型来实现。

    fun testDispatchers() {
        try {
            val defaultJob = GlobalScope.launch(context = Dispatchers.Default) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }

            val mainJob = GlobalScope.launch(context = Dispatchers.Main) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--mainJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--mainJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--mainJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }

            val unconfinedJob = GlobalScope.launch(context = Dispatchers.Unconfined) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--unconfinedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--unconfinedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--unconfinedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }

            val ioJob = GlobalScope.launch(context = Dispatchers.IO) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--ioJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--ioJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--ioJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }
        } catch (e: Exception) {
            println(e.localizedMessage)
        }
    }

输出:

  • Dispatchers.Default,执行任务时,使用默认的协程线程池,线程个数为CPU个数 但不能少于2个。
    dispatchers_defalut
  • Dispatchers.Main,执行任务时,使用Android默认的UI线程。
    dispatchers_main
  • Dispatchers.Unconfined,执行任务时,使用当前线程。
    dispatchers_unconfined
  • Dispatchers.IO,执行任务时,使用默认的IO线程池,线程个数为Math.max(64,CPU个数)
    dispatch_io

默认的CoroutineStart

构建协程时,使用CoroutineStart中定义的类型来实现。

    fun testCoroutineStart() {
        try {
            val defaultJob = GlobalScope.launch(start = CoroutineStart.DEFAULT) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--defaultJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }

            val lazyJob = GlobalScope.launch(start = CoroutineStart.LAZY) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--lazyJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--lazyJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--lazyJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }
            lazyJob.start()

            val atomicJob = GlobalScope.launch(start = CoroutineStart.ATOMIC) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--atomicJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--atomicJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--atomicJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }
            atomicJob.cancel()

            val undispatchedJob = GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--undispatchedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    Log.e("ViewModel", "launch--undispatchedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--undispatchedJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }
        } catch (e: Exception) {
            println(e.localizedMessage)
        }
    }

输出:

  • CoroutineStart.DEFAULT,默认的启动模式,会直接开始执行任务,不需要单独调用job.start()
    coroutine_start_default
  • CoroutineStart.LAZY,懒加载模式,只有在调用了job.start()才会开始执行协程任务。
    coroutine_start_lazy
  • CoroutineStart.ATOMIC,原子模式,会直接开始执行任务,但取消任务时,如果任务还没开始执行,会取消不了任务。
    coroutine_start_atomic
  • CoroutineStart.UNDISPATCHED,不调度模式,会直接开始执行任务,但切换协程被挂起(切换线程)之后,再次resume时不会切换到之前启动的线程,而是以当前执行的线程去继续执行任务 。
    coroutine_start_undispatched

取消协程

协程构建之后,会返回Job对象,可以用来取消协程。

Job.cancel(cause: Throwable? = null): Boolean

抛出指定的异常来取消协程,如果传递空则抛出CancellationException

    fun testCancelCoroutine() {
        try {
            Log.e("ViewModel", "testCancelCoroutine")
            val defaultCancelJob = GlobalScope.launch { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--defaultCancelJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    delay(10000)//延时10s
                    Log.e("ViewModel", "launch--defaultCancelJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--defaultCancelJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }
            defaultCancelJob.cancel()

        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

输出:
coroutine_cancel

Job.cancelAndJoin()

等到协程任务被执行完成,才会取消。

    fun testCancelAndJoinCoroutine() {
        try {
            Log.e("ViewModel", "testCancelAndJoinCoroutine")
            val defaultCancelAndJoinJob = GlobalScope.launch { // 在后台启动一个新的协程并继续
                Log.e("ViewModel", "start--defaultCancelAndJoinJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                withContext(context = Dispatchers.IO) {
                    delay(10000)//延时10s
                    Log.e("ViewModel", "launch--defaultCancelAndJoinJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
                }
                Log.e("ViewModel", "after--defaultCancelAndJoinJob:${Thread.currentThread().name};${Thread.currentThread().id};${coroutineContext[Job]}")
            }
            launch {
                defaultCancelAndJoinJob.cancelAndJoin()
                Log.e("ViewModel", "cancelAndJoin")
            }

        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

输出:
coroutine_cancel_and_join

### Kotlin 协程中的 Job 使用与管理 在 Kotlin 协程框架中,`Job` 是用于表示协程生命周期的对象。通过 `Job` 可以启动、取消以及监控协程的状态。 #### 创建并启动带有 Job 的协程 当创建个新的协程时,默认情况下会关联到个 `Job` 实例。可以显式获取这个 `Job` 来对其进行操作: ```kotlin import kotlinx.coroutines.* fun main() { val job = GlobalScope.launch { println("Coroutine is running") } Thread.sleep(100L) // 主线程等待段时间让协程执行完毕 } ``` #### 获取当前协程的 Job 对象 如果想要在个已经存在的协程内部访问其自身的 `Job` ,可以通过 `coroutineContext[Job]` 或者更简单的 `job()` 函数来实现: ```kotlin val currentJob: Job? get() = coroutineContext[Job] // or simply use 'job()' inside a suspending function. println(currentJob?.isActive ?: "No active job found.") ``` #### 取消和加入 Job 为了优雅地停止某个特定的任务或者确保组子任务全部完成后继续下步工作,提供了两种方法——`cancel()` 和 `join()` : - **Cancel**: 请求终止指定的作业及其所有子作业。 ```kotlin import kotlinx.coroutines.* fun main() = runBlocking<Unit> { val job = launch { repeat(1000) { i -> println("job: I'm sleeping $i ...") delay(500L) } } delay(1300L) // 延迟会儿... println("main: I'm tired of waiting!") job.cancel() job.join() println("main: Now I can quit.") } ``` - **Join**: 阻塞调用方直到目标作业完成为止。 ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { val job = async { doSomethingUsefulOne() doSomethingUsefulTwo() } // 等待异步任务结束再打印消息 job.join() println("The answer was ${job.getCompleted()}") } suspend fun doSomethingUsefulOne(): Int {...} // 返回整数的结果 suspend fun doSomethingUsefulTwo(): Int {...} // 同上 ``` #### 监听 Job 生命周期事件 还可以监听 `Job` 的状态变化以便更好地控制程序流程或响应异常情况的发生。这通常涉及到使用回调机制如 `invokeOnCompletion`. ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { val job = launch(start = CoroutineStart.LAZY) { try { println("Child says: I feel sleepy...") yield() println("...but I will work now") TODO("Do some real work here") } finally { println("Child is finished with result '${currentCoroutineContext()[Job]?.isCancelled}'") } } job.invokeOnCompletion { cause -> if (cause != null && !job.isCancelled) { println("Child has failed with an exception") } else { println("Child completed normally") } } job.start() job.join() } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值