kotlin之协程(Coroutine)使用<二>

kotlin之协程(Coroutine)是什么<一>

kotlin之协程(Coroutine)使用<二>

协程(Coroutine)是是什么?

协程(Coroutine)处理了什么问题?

协程(Coroutine)怎么使用的?

懵逼三连!!!! 一步步来吧,又是坑壁掉头发的一天.

懵逼三连第二弹,协程使用走起


1.协程实现并发优缺点

协程 是单线程实现并发的一种技术,具有轻量级 ,高并发 ,线程安全的特点.

优点
  1. 轻量级 :协程是轻量级线程,创建、销毁开销远小于线程。如 Python 的 asyncio 库可轻松创建成千上万个协程,上下文切换无需复杂的内核态和用户态切换,速度极快,能在单线程内高效处理大量并发任务。
  2. 高并发处理能力:处理 I/O 密集型任务表现出色。以网络爬虫为例,协程发起网络请求后会主动让出执行权,让其他协程执行,避免阻塞,提高系统并发性能。
  3. 避免锁机制:采用协作式调度,同一时间只有一个协程执行,无数据竞争问题,无需锁机制,代码逻辑清晰,减少死锁和锁竞争带来的性能损耗,降低编程复杂度。
  4. 代码可读性好 :可让异步代码以同步方式编写。如 Kotlin 中通过 suspend 函数封装异步操作,使代码执行流程直观,便于理解和维护。
缺点
  1. 单线程瓶颈:通常在单线程内运行,若线程被 CPU 密集型任务阻塞,所有协程都会受影响。如在协程中进行大量数学计算,会导致其他协程无法及时执行。
  2. 调试困难:执行流程复杂,多个协程切换和通信时,难以像调试同步代码那样直观定位问题,确定异常协程较难。
  3. 依赖特定编程模型:不同编程语言对协程支持和实现方式不同,开发者需学习特定编程模型和语法,增加学习成本。
使用场景
  • I/O 密集型任务:如网络爬虫、Web 服务器处理请求、数据库读写等场景,能在等待 I/O 操作时高效切换协程,提升整体性能。
  • 对资源敏感的环境:在资源有限的设备上,协程轻量级的特性使其可以创建大量并发任务而不会过度消耗系统资源。

2.Kotlin 协程使用

  • 协程构建器 :用于启动协程的函数,常见的有 launchasync 等。
  • 作用域 :协程必须在一个协程作用域内运行,作用域可以管理协程的生命周期,常见的作用域有 GlobalScopeCoroutineScope 等。
  • 挂起函数 :使用 suspend 关键字修饰的函数,它可以在协程中被调用,并且可以暂停协程的执行,等待某个操作完成后再恢复执行。

依赖配置:

如果你使用的是 Android 项目,需要在 build.gradle 文件中添加以下依赖:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'

2.1 协程构建器 launchasync,runBlocking,withContext用于启动协程函数
  • launch 用于启动一个新的协程,不返回结果,主要用于执行一些不需要返回值的异步任务

  • GlobalScope.launch{}

  • runBlocking {}

  • MainScope().launch {}

  • lifecycleScope.launch {}

  • viewModelScope.launch {}

    scss 复制代码
    fun main() {
    // 使用 GlobalScope.launch 启动一个协程
    GlobalScope.launch { 
        println("协程开始执行")
        delay(1000) // 模拟耗时操作
        println("协程执行结束")
    }
    
    println("主线程继续执行")
    Thread.sleep(2000) // 防止主线程提前结束
    }
  • async 用于启动一个新的协程,并且可以返回一个 Deferred 对象,通过调用 await() 方法可以获取协程的执行结果。

    kotlin 复制代码
    import kotlinx.coroutines.*
    
    fun main() = runBlocking {
     // 使用 async 启动一个协程
       val deferred = async { 
           delay(1000)
           42
       }
       // 获取协程的执行结果
      val result = deferred.await() 
      println("协程执行结果: $result")
    }
  • runBlocking它会运行一个新的协程并阻塞当前线程,直到协程完成。 import kotlinx.coroutines.*

    scss 复制代码
    fun main() = runBlocking {
        println("runBlocking start")
        // 在阻塞协程中启动一个子协程
        launch {
           println("launch start")
            delay(1000) // 模拟耗时操作
            println("launch end")
        }
        println("runBlocking end")
    }

上述代码中,主线程在进入runBlocking后被阻塞,直到runBlocking内部的子协程完成后才继续执行。

源码之: BuildersKt 启动器源码分析

kotlin 复制代码
package kotlinx.coroutines

/**
 * 启动一个新的协程,并阻塞当前线程,直到协程执行完毕。
 * 通常用于将非协程的代码与协程代码桥接,例如在 `main` 函数中启动协程。
 *
 * @param context 协程上下文,可指定协程运行的调度器、异常处理器等,默认使用默认的上下文。
 * @param block 协程要执行的挂起函数块,其结果类型为 `T`。
 * @return 返回协程块执行的结果。
 */
public fun <T> runBlocking(context: kotlin.coroutines.CoroutineContext = COMPILED_CODE, block: suspend kotlinx.coroutines.CoroutineScope.() -> T): T { 
    contract { /* compiled contract */ }; /* compiled code */ 
}

/**
 * 表示协程的已恢复状态,是一个私有常量,在协程内部状态管理中使用。
 */
private const val RESUMED: kotlin.Int /* compiled code */

/**
 * 表示协程的已挂起状态,是一个私有常量,用于协程内部状态管理。
 */
private const val SUSPENDED: kotlin.Int /* compiled code */

/**
 * 表示协程的未决定状态,是一个私有常量,在协程启动和状态转变过程中使用。
 */
private const val UNDECIDED: kotlin.Int /* compiled code */

/**
 * 在指定的协程上下文中执行一个挂起函数块,并返回结果。
 * 此函数可以切换协程的上下文,例如从一个调度器切换到另一个调度器。
 *
 * @param context 要切换到的协程上下文。
 * @param block 要在新上下文中执行的挂起函数块。
 * @return 返回挂起函数块执行的结果。
 */
public suspend fun <T> withContext(context: kotlin.coroutines.CoroutineContext, block: suspend kotlinx.coroutines.CoroutineScope.() -> T): T { 
    contract { /* compiled contract */ }; /* compiled code */ 
}

/**
 * 在给定的协程作用域内异步启动一个新的协程,并返回一个 `Deferred` 对象,
 * 用于获取协程的执行结果。
 *
 * @param context 协程上下文,可指定协程运行的调度器、异常处理器等,默认使用默认的上下文。
 * @param start 协程的启动模式,默认是延迟启动模式。
 * @param block 协程要执行的挂起函数块,其结果类型为 `T`。
 * @return 返回一个 `Deferred` 对象,通过调用其 `await()` 方法可以获取协程的执行结果。
 */
public fun <T> kotlinx.coroutines.CoroutineScope.async(context: kotlin.coroutines.CoroutineContext = COMPILED_CODE, start: kotlinx.coroutines.CoroutineStart = COMPILED_CODE, block: suspend kotlinx.coroutines.CoroutineScope.() -> T): kotlinx.coroutines.Deferred<T> { 
    /* compiled code */ 
}

/**
 * 在协程调度器上执行一个挂起函数块。
 * 此函数允许在特定的调度器上运行协程代码。
 *
 * @param block 要在调度器上执行的挂起函数块。
 * @return 返回挂起函数块执行的结果。
 */
public suspend inline operator fun <T> kotlinx.coroutines.CoroutineDispatcher.invoke(noinline block: suspend kotlinx.coroutines.CoroutineScope.() -> T): T { 
    /* compiled code */ 
}

/**
 * 在给定的协程作用域内启动一个新的协程,不返回结果。
 * 通常用于执行不需要返回值的异步任务。
 *
 * @param context 协程上下文,可指定协程运行的调度器、异常处理器等,默认使用默认的上下文。
 * @param start 协程的启动模式,默认是立即启动模式。
 * @param block 协程要执行的挂起函数块。
 * @return 返回一个 `Job` 对象,可用于管理协程的生命周期,例如取消协程。
 */
public fun kotlinx.coroutines.CoroutineScope.launch(context: kotlin.coroutines.CoroutineContext = COMPILED_CODE, start: kotlinx.coroutines.CoroutineStart = COMPILED_CODE, block: suspend kotlinx.coroutines.CoroutineScope.() -> kotlin.Unit): kotlinx.coroutines.Job { 
    /* compiled code */ 
}

源码之: BuildersKt_Builders_commonKt.class 启动器源码分析

kotlin 复制代码
    package kotlinx.coroutines

    import kotlinx.coroutines.intrinsics.*
    import kotlin.coroutines.*
    import kotlin.coroutines.intrinsics.*

    // --------------- launch ---------------
    /**
     * 在指定的 CoroutineScope 中启动一个新的协程,不阻塞当前线程,返回一个 Job 用于控制协程。
     *
     * @param context 协程的额外上下文,默认为空上下文。
     * @param start 协程的启动模式,默认为立即启动。
     * @param block 协程要执行的挂起代码块。
     */
    public fun CoroutineScope.launch(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> Unit
    ): Job {
        // 合并当前 CoroutineScope 的上下文和传入的上下文
        val newContext = newCoroutineContext(context)
        // 根据启动模式创建不同类型的协程对象
        val coroutine = if (start.isLazy)
            LazyStandaloneCoroutine(newContext, block) else
            StandaloneCoroutine(newContext, active = true)
        // 启动协程
        coroutine.start(start, coroutine, block)
        return coroutine
    }

    // --------------- async ---------------
    /**
     * 在指定的 CoroutineScope 中启动一个新的协程,并返回一个 Deferred 对象用于获取协程的结果。
     *
     * @param context 协程的额外上下文,默认为空上下文。
     * @param start 协程的启动模式,默认为立即启动。
     * @param block 协程要执行的挂起代码块,返回结果类型为 T。
     */
    public fun <T> CoroutineScope.async(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> T
    ): Deferred<T> {
        // 合并当前 CoroutineScope 的上下文和传入的上下文
        val newContext = newCoroutineContext(context)
        // 根据启动模式创建不同类型的协程对象
        val coroutine = if (start.isLazy)
            LazyDeferredCoroutine(newContext, block) else
            DeferredCoroutine<T>(newContext, active = true)
        // 启动协程
        coroutine.start(start, coroutine, block)
        return coroutine
    }

    // 实现 Deferred 接口的协程类
    private open class DeferredCoroutine<T>(
        parentContext: CoroutineContext,
        active: Boolean
    ) : AbstractCoroutine<T>(parentContext, true, active = active), Deferred<T> {
        // 获取协程已完成的结果
        override fun getCompleted(): T = getCompletedInternal() as T
        // 挂起等待协程完成并返回结果
        override suspend fun await(): T = awaitInternal() as T
        // 用于 select 表达式的等待子句
        override val onAwait: SelectClause1<T> get() = onAwaitInternal as SelectClause1<T>
    }

    // 懒启动的 Deferred 协程类
    private class LazyDeferredCoroutine<T>(
        parentContext: CoroutineContext,
        block: suspend CoroutineScope.() -> T
    ) : DeferredCoroutine<T>(parentContext, active = false) {
        // 创建未拦截的协程
        private val continuation = block.createCoroutineUnintercepted(this, this)

        // 启动协程
        override fun onStart() {
            continuation.startCoroutineCancellable(this)
        }
    }

    // --------------- withContext ---------------
    /**
     * 在指定的协程上下文中执行挂起代码块,挂起当前协程直到代码块执行完成并返回结果。
     *
     * @param context 要切换到的协程上下文。
     * @param block 要执行的挂起代码块。
     */
    public suspend fun <T> withContext(
        context: CoroutineContext,
        block: suspend CoroutineScope.() -> T
    ): T {
        return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
            // 获取当前协程的上下文
            val oldContext = uCont.context
            // 合并当前上下文和传入的上下文
            val newContext = oldContext.newCoroutineContext(context)
            // 确保新上下文是活跃的
            newContext.ensureActive()

            // 快速路径 1:新上下文和旧上下文相同
            if (newContext === oldContext) {
                val coroutine = ScopeCoroutine(newContext, uCont)
                return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
            }

            // 快速路径 2:新调度器和旧调度器相同
            if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
                val coroutine = UndispatchedCoroutine(newContext, uCont)
                withCoroutineContext(coroutine.context, null) {
                    return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
                }
            }

            // 慢路径:使用新的调度器
            val coroutine = DispatchedCoroutine(newContext, uCont)
            block.startCoroutineCancellable(coroutine, coroutine)
            coroutine.getResult()
        }
    }

    /**
     * 调用 withContext 函数,在指定的 CoroutineDispatcher 中执行挂起代码块。
     *
     * @param block 要执行的挂起代码块。
     */
    public suspend inline operator fun <T> CoroutineDispatcher.invoke(
        noinline block: suspend CoroutineScope.() -> T
    ): T = withContext(this, block)

    // 独立协程类
    private open class StandaloneCoroutine(
        parentContext: CoroutineContext,
        active: Boolean
    ) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) {
        // 处理协程中的异常
        override fun handleJobException(exception: Throwable): Boolean {
            handleCoroutineException(context, exception)
            return true
        }
    }

    // 懒启动的独立协程类
    private class LazyStandaloneCoroutine(
        parentContext: CoroutineContext,
        block: suspend CoroutineScope.() -> Unit
    ) : StandaloneCoroutine(parentContext, active = false) {
        // 创建未拦截的协程
        private val continuation = block.createCoroutineUnintercepted(this, this)

        // 启动协程
        override fun onStart() {
            continuation.startCoroutineCancellable(this)
        }
    }

    // 用于 withContext 中上下文改变但调度器不变的情况
    internal expect class UndispatchedCoroutine<in T>(
        context: CoroutineContext,
        uCont: Continuation<T>
    ) : ScopeCoroutine<T>

    // 用于 withContext 中上下文调度器改变的情况
    @PublishedApi
    internal class DispatchedCoroutine<in T> internal constructor(
        context: CoroutineContext,
        uCont: Continuation<T>
    ) : ScopeCoroutine<T>(context, uCont) {
        // 协程状态,0 表示未决定,1 表示挂起,2 表示恢复
        @JvmField
        public val _decision = atomic(UNDECIDED)

        // 尝试将协程挂起
        private fun trySuspend(): Boolean {
            _decision.loop { decision ->
                when (decision) {
                    UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, SUSPENDED)) return true
                    RESUMED -> return false
                    else -> error("Already suspended")
                }
            }
        }

        // 尝试恢复协程
        private fun tryResume(): Boolean {
            _decision.loop { decision ->
                when (decision) {
                    UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, RESUMED)) return true
                    SUSPENDED -> return false
                    else -> error("Already resumed")
                }
            }
        }

        // 协程完成后的处理
        override fun afterCompletion(state: Any?) {
            afterResume(state)
        }

        // 协程恢复后的处理
        override fun afterResume(state: Any?) {
            if (tryResume()) return 
            uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))
        }

        // 获取协程的结果
        internal fun getResult(): Any? {
            if (trySuspend()) return COROUTINE_SUSPENDED
            val state = this.state.unboxState()
            if (state is CompletedExceptionally) throw state.cause
            return state as T
        }
    }

    private const val UNDECIDED = 0
    private const val SUSPENDED = 1
    private const val RESUMED = 2

源码之: BuildersKt_BuildersKt.class 启动器源码分析

kotlin 复制代码
    ```
package kotlinx.coroutines

import kotlin.contracts.*
import kotlin.coroutines.*

/**
 * 运行一个新的协程并阻塞当前线程,直到协程完成,该阻塞操作可被中断。
 * 此函数不应在协程内部使用,主要用于连接常规阻塞代码和使用挂起风格编写的库,
 * 常用于 `main` 函数和测试中。
 *
 * @param context 协程的上下文,默认值是当前线程上的事件循环。
 * @param block 协程要执行的代码块。
 */
@Throws(InterruptedException::class)
public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
    // 契约声明:表明 block 代码块只会被精确调用一次
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    // 获取当前正在执行的线程
    val currentThread = Thread.currentThread()
    // 从传入的协程上下文中获取延续拦截器
    val contextInterceptor = context[ContinuationInterceptor]
    var eventLoop: EventLoop?
    var newContext: CoroutineContext
    // 如果上下文中没有指定调度器
    if (contextInterceptor == null) {
        // 创建或使用线程本地的事件循环
        eventLoop = ThreadLocalEventLoop.eventLoop
        // 合并上下文,添加事件循环
        newContext = GlobalScope.newCoroutineContext(context + eventLoop)
    } else {
        // 检查上下文的拦截器是否是事件循环,或者使用现有的线程本地事件循环
        eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() }
            ?: ThreadLocalEventLoop.currentOrNull()
        // 创建新的协程上下文
        newContext = GlobalScope.newCoroutineContext(context)
    }
    // 创建一个阻塞协程实例
    val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
    // 以默认启动模式启动协程
    coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
    // 阻塞当前线程,直到协程完成并返回结果
    return coroutine.joinBlocking()
}

/**
 * 用于 `runBlocking` 的阻塞协程类。
 *
 * @param parentContext 父协程上下文。
 * @param blockedThread 被阻塞的线程。
 * @param eventLoop 事件循环。
 */
private class BlockingCoroutine<T>(
    parentContext: CoroutineContext,
    private val blockedThread: Thread,
    private val eventLoop: EventLoop?
) : AbstractCoroutine<T>(parentContext, true, true) {

    // 标记为有作用域的协程
    override val isScopedCoroutine: Boolean get() = true

    /**
     * 协程完成后的回调方法。
     * 如果当前线程不是被阻塞的线程,则唤醒被阻塞的线程。
     *
     * @param state 协程的状态。
     */
    override fun afterCompletion(state: Any?) {
        if (Thread.currentThread() != blockedThread) {
            // 唤醒被阻塞的线程
            unpark(blockedThread)
        }
    }

    /**
     * 阻塞当前线程,直到协程完成并返回结果。
     *
     * @return 协程的执行结果。
     */
    @Suppress("UNCHECKED_CAST")
    fun joinBlocking(): T {
        // 注册为时间循环线程
        registerTimeLoopThread()
        try {
            // 增加事件循环的使用计数
            eventLoop?.incrementUseCount()
            try {
                while (true) {
                    // 检查当前线程是否被中断,如果被中断则抛出 InterruptedException 并取消协程
                    if (Thread.interrupted()) {
                        throw InterruptedException().also { cancelCoroutine(it) }
                    }
                    // 处理下一个事件并返回下一次处理的延迟时间
                    val parkNanos = eventLoop?.processNextEvent() ?: Long.MAX_VALUE
                    // 如果协程已经完成,则跳出循环
                    if (isCompleted) break
                    // 阻塞当前线程指定的时间
                    parkNanos(this, parkNanos)
                }
            } finally {
                // 减少事件循环的使用计数
                eventLoop?.decrementUseCount()
            }
        } finally {
            // 取消时间循环线程的注册
            unregisterTimeLoopThread()
        }
        // 获取协程的状态
        val state = this.state.unboxState()
        // 如果状态是 CompletedExceptionally,抛出异常
        (state as? CompletedExceptionally)?.let { throw it.cause }
        // 返回协程的结果
        return state as T
    }
}
```
2.2 协程组成
  • CoroutineContext 可看作是协程执行的环境
  • CoroutineScope协程的生命周期
  • suspend挂起函数
2.2.1 CoroutineContext 即协程上下文

CoroutineContext 是一个接口,它是一个包含了各种协程参数和配置信息的集合,可看作是协程执行的环境,为协程提供各种信息和支持。它类似于一个键值对的集合,其中的键由 CoroutineContext.Key 定义,每个元素都有一个唯一的键。

  • 组成元素

    • Job:用于管理协程的生命周期和状态,比如可以通过它的 cancel () 函数取消协程,通过 isActive 等属性判断协程状态。

      注意:

      SupervisorJob:使用 SupervisorJob 时,一个子协程的失败不会影响其他子协程和父协程(特殊的Job)

    • CoroutineDispatcher:协程调度器,用于决定协程在哪个线程或线程池上执行,如 Dispatchers.Main 表示在主线程执行,Dispatchers.IO 用于 I/O 密集型任务,在 IO 线程池执行,Dispatchers.Default 适用于 CPU 密集型任务等。

    • CoroutineExceptionHandler:协程的异常处理器,用于统一捕获协程中抛出的未处理异常,可在其中定义对异常的处理逻辑。

    • CoroutineName:用于给协程命名,方便在调试或日志记录时识别不同的协程。

    • ContinuationInterceptor:拦截器,用于拦截协程的执行流程,可以在协程开始前、执行中、执行后进行一些特定操作。

2.2.1 CoroutineScope(协程作用域)

CoroutineScope 是一个接口,代表了一个协程的作用范围,它可以看作是协程的容器。通过 CoroutineScope,可以启动多个协程,并且这些协程会受到该作用域的管理。当作用域被取消时,其内部所有协程也会被取消

作用

  • 生命周期管理:可以将协程的生命周期与特定的业务逻辑、UI 组件等关联起来。例如,在 Android 开发中,可以将协程的生命周期与 Activity 或 Fragment 的生命周期绑定,当 Activity 或 Fragment 销毁时,自动取消其内部的协程,避免内存泄漏。
  • 结构化并发 :确保协程的启动和取消是有序的,避免出现协程泄漏的问题。在一个 CoroutineScope 内启动的协程,它们的执行和取消是相互关联的,符合结构化并发的原则。

示例:

kotlin 复制代码
```
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

fun main() {
    val scope = CoroutineScope(Dispatchers.Default)
    scope.launch {
        // 协程执行的代码
        println("Custom scope coroutine is running.")
    }
    Thread.sleep(1000) // 为了保证协程有时间执行
    scope.cancel() // 取消作用域,其内部协程也会被取消
}
```
2.2.3 suspend挂起函数 (协程的核心)

关键字suspend修饰的函数。它可以在执行过程中暂停自身的执行,将控制权交还给调用者,并且在合适的时候恢复执行

从底层来看,挂起函数在编译时会被转换为状态机。当挂起函数挂起时,状态机会保存当前的执行状态;当满足恢复条件时,状态机会根据保存的状态从挂起的位置继续执行。这种状态机的实现方式使得挂起函数可以在不阻塞线程的情况下实现异步操作。

特点

  1. 可暂停和恢复 :挂起函数在执行过程中可以暂停,不会阻塞当前线程。当满足特定条件(如异步操作完成)时,它可以从暂停的位置继续执行。例如,上述的 fetchData 函数调用 delay 函数时会挂起,1 秒后恢复执行。
  2. 只能在协程或其他挂起函数中调用:由于挂起函数的挂起和恢复机制依赖于协程上下文,所以它不能在普通函数中直接调用,只能在协程或者另一个挂起函数中被调用。

个人理解

suspend挂起函数调用的时候会将自身挂起来不阻碍调用线程的执行,当挂起函数执行完毕的时候会在调用位置返回结果,类似回调.

示例:

kotlin 复制代码
    import kotlinx.coroutines.*

    // 自定义挂起函数,模拟网络请求
    suspend fun fetchData(): String {
        delay(2000) // 模拟 2 秒的网络请求耗时
        return "Data fetched from network"
    }

    fun main() = runBlocking {
        // 创建自定义作用域,使用 Dispatchers.Default 调度器
        val customScope = CoroutineScope(Dispatchers.Default)

        // 在自定义作用域中启动一个协程,调用挂起函数
        customScope.launch {
            val result = fetchData()
            println(result)
        }

        // 等待一段时间,确保协程有足够时间执行
        delay(3000)

        // 取消自定义作用域,其中的协程也会被取消
        customScope.cancel()
        }

3.协程的使用

kotlin 复制代码
package com.example.coroutinedemo

import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

class MainActivity : AppCompatActivity() {

    private lateinit var startButton: Button
    private lateinit var resultTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentViewuser
        setContentView("activity_main")

        startButton = findViewById(R.id.startButton)
        resultTextView = findViewById(R.id.resultTextView)

        startButton.setOnClickListener {
            // 使用 GlobalScope 启动协程
            GlobalScope.launch(Dispatchers.IO) {
                val globalScopeResult = performTask()
                withContext(Dispatchers.Main) {
                    resultTextView.text = "GlobalScope result: $globalScopeResult"
                }
            }

            // 使用 lifecycleScope 启动协程
            lifecycleScope.launch {
                val lifecycleScopeResult = performTask()
                resultTextView.text = "${resultTextView.text}\nLifecycleScope result: $lifecycleScopeResult"
            }

            // 使用 MainScope 启动协程
            val mainScope = MainScope()
            mainScope.launch {
                val mainScopeResult = performTask()
                resultTextView.text = "${resultTextView.text}\nMainScope result: $mainScopeResult"
            }

            // 在 ViewModel 中使用 viewModelScope
            val viewModel = MainViewModel()
            viewModelScope.launch {
                val viewModelScopeResult = viewModel.performViewModelTask()
                resultTextView.text = "${resultTextView.text}\nViewModelScope result: $viewModelScopeResult"
            }

            // 演示 launch 和 async 的使用
            lifecycleScope.launch {
                val time = measureTimeMillis {
                    // 使用 launch 启动协程
                    val job = launch(Dispatchers.IO) {
                        val launchResult = performTask()
                        withContext(Dispatchers.Main) {
                            resultTextView.text = "${resultTextView.text}\nLaunch result: $launchResult"
                        }
                    }

                    // 使用 async 启动异步任务
                    val deferred = async(Dispatchers.IO) {
                        performTask()
                    }
                    val asyncResult = deferred.await()
                    withContext(Dispatchers.Main) {
                        resultTextView.text = "${resultTextView.text}\nAsync result: $asyncResult"
                    }

                    job.join()
                }
                resultTextView.text = "${resultTextView.text}\nTotal time: $time ms"
            }
        }
    }

    private suspend fun performTask(): String {
        delay(1000) // 模拟耗时操作
        return "Task completed"
    }
}

写在结尾

输入头发-------coding------->输出结果

协程是单线程内处理并发的一种技术,在kotlin通过构造器创建协程 通过CoroutineScope定义生命周期,CoroutineContext即协程上下文(job,Dispatcher等容器),执行suspend挂起函数来实现单线程内的并发操作.

相关推荐
YZhou_xu2 小时前
稳定Android Studio2021.1.2.16的安装
android·ide·android studio
xiyangyang81102 小时前
Android studio ternimal 中gradle 指令失效(gradle环境变量未配置)
android·ide·android studio
Crazy程序猿20202 小时前
Apk反编译实现步骤
android·反编译
消失的旧时光-19433 小时前
Android 音视频编解码 -- MediaCodec
android·视频编解码
左灯右行的爱情4 小时前
深度整理总结MySQL——Order By的工作原理
android·数据库·mysql
未来之窗软件服务4 小时前
构建 Android Archive (AAR) 文件时,不支持直接引用本地的 .aar
android
ChinaRainbowSea6 小时前
十. Redis 事务和 “锁机制”——> 并发秒杀处理的详细说明
android·java·数据库·redis·后端·bootstrap·nosql
醒了就刷牙6 小时前
整理:熟悉MySQL的使用和运行原理,掌握索引、事务、锁等机制。了解存储引擎、读写分离、分库分表。
android·数据库·mysql
dal118网工任子仪15 小时前
93,【1】buuctf web [网鼎杯 2020 朱雀组]phpweb
android·前端