本文面向 Kotlin 协程初学者,从底层原理到实战应用,全面讲解协程在 Kuikly 中的使用
引言:为什么需要协程
想象一个场景:你需要先获取用户信息,再根据用户信息获取订单列表,最后更新 UI。
传统回调方式:
// 回调地狱 😱
getUserInfo { user ->
getOrderList(user.id) { orders ->
getOrderDetail(orders[0].id) { detail ->
// 终于拿到数据了...
updateUI(detail)
}
}
}
这种代码的问题:
- 嵌套层级深:难以阅读和维护
- 错误处理困难:每层都需要处理错误
- 无法方便地取消:如果用户离开页面,这些回调可能仍在执行
协程方式:
// 清晰优雅 ✨
lifecycleScope.launch {
try {
val user = getUserInfo()
val orders = getOrderList(user.id)
val detail = getOrderDetail(orders[0].id)
updateUI(detail)
} catch (e: Exception) {
showError(e)
}
}
协程让异步代码看起来像同步代码,这就是它的魔力!
第一部分:协程的本质
什么是协程
协程(Coroutine)= Co(协作)+ Routine(例程/函数)
简单说,协程是一种可以暂停和恢复执行的函数。与普通函数不同,协程在执行过程中可以"暂停",把控制权交给其他代码,等条件满足后再"恢复"执行。
┌──────────────────────────────────────────────────────────┐
│ 普通函数执行流程 │
├──────────────────────────────────────────────────────────┤
│ │
│ 开始 ──────────────────────────────────────────► 结束 │
│ (一口气执行完,不能中断) │
│ │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ 协程执行流程 │
├──────────────────────────────────────────────────────────┤
│ │
│ 开始 ────► 挂起点1 ····暂停···· 恢复 ────► 挂起点2 ···· │
│ │
│ ····暂停···· 恢复 ──────────────────► 结束 │
│ │
└──────────────────────────────────────────────────────────┘
协程 vs 线程
| 特性 | 线程 | 协程 |
|---|---|---|
| 调度者 | 操作系统 | 程序自身 |
| 创建成本 | 高(约 1MB 栈内存) | 低(约几十字节) |
| 切换成本 | 高(内核态切换) | 低(用户态切换) |
| 数量限制 | 通常几千个 | 可以创建数十万个 |
| 阻塞影响 | 阻塞整个线程 | 只暂停当前协程 |
关键区别:阻塞 vs 挂起
// 线程阻塞:整个线程被占用,不能做其他事
Thread.sleep(1000) // 线程在这 1 秒内什么都不能做
// 协程挂起:只是暂停协程,线程可以去执行其他任务
delay(1000) // 协程暂停,线程可以去执行其他协程
挂起函数(suspend)的魔法
suspend 关键字是 Kotlin 协程的核心。它告诉编译器:这个函数可能会暂停执行。
// 普通函数
fun normalFunction(): String {
return "Hello"
}
// 挂起函数
suspend fun suspendFunction(): String {
delay(1000) // 可以调用其他挂起函数
return "Hello after 1 second"
}
suspend 的本质是什么?
编译器会将挂起函数转换为带有 Continuation 参数的函数:
// 你写的代码
suspend fun fetchData(): String {
delay(1000)
return "data"
}
// 编译器转换后(简化版)
fun fetchData(continuation: Continuation<String>): Any {
// ... 状态机代码
}
Continuation(续体)就是"接下来要做什么"的封装。这是协程实现的关键,我们稍后会详细讲解。
第二部分:Kotlin 协程核心概念
Continuation:协程的灵魂
Continuation 是 Kotlin 标准库中定义的接口:
// Kotlin 标准库中的定义
public interface Continuation<in T> {
// 协程的上下文
public val context: CoroutineContext
// 恢复协程执行,传入结果
public fun resumeWith(result: Result<T>)
}
Continuation 做什么?
它封装了两个东西:
- 协程的上下文:包含调度器、Job 等信息
- 恢复执行的能力:通过
resumeWith让协程继续运行
让我们用一个比喻理解它:
想象你在读一本书,读到一半要去做饭:
普通函数:
- 合上书,做完饭后忘了读到哪里了
协程 + Continuation:
- 在书里夹一个书签(Continuation)
- 书签记录了:当前页码、你的阅读进度、理解状态
- 做完饭后,打开书签就能继续读
两个常用的扩展函数:
// 成功恢复
fun <T> Continuation<T>.resume(value: T) {
resumeWith(Result.success(value))
}
// 异常恢复
fun <T> Continuation<T>.resumeWithException(exception: Throwable) {
resumeWith(Result.failure(exception))
}
CoroutineScope:协程的作用域
CoroutineScope 定义了协程的生命周期范围:
// Kuikly 中的定义
interface CoroutineScope {
val coroutineContext: CoroutineContext
}
为什么需要作用域?
作用域解决了协程的生命周期管理问题:
// 问题:用户离开页面后,协程还在运行
GlobalScope.launch {
while (true) {
delay(1000)
updateUI() // 页面都销毁了,还在更新 UI!
}
}
// 解决:使用绑定生命周期的作用域
lifecycleScope.launch {
while (true) {
delay(1000)
updateUI() // 页面销毁时,协程自动取消
}
}
Job:协程的生命周期
Job 是协程的句柄,用于管理协程的生命周期:
// Kuikly 中的定义
interface Job : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<Job>
}
launch 函数返回一个 Job,你可以用它来控制协程:
val job = GlobalScope.launch {
repeat(1000) { i ->
println("执行中: $i")
delay(500)
}
}
// 稍后取消协程
job.cancel()
Deferred:带返回值的协程
Deferred 继承自 Job,额外提供了获取返回值的能力:
// Kuikly 中的定义
interface Deferred<out T> : Job {
suspend fun await(): T // 等待结果
}
使用示例:
// async 返回 Deferred
val deferred: Deferred<String> = GlobalScope.async {
delay(1000)
"计算结果"
}
// 在其他地方等待结果
GlobalScope.launch {
val result = deferred.await() // 挂起直到结果可用
println(result) // 输出:计算结果
}
第三部分:Kuikly 协程实现剖析
为什么 Kuikly 要自己实现协程
Kuikly 实现了一套简化版的协程系统,而不是直接使用 kotlinx.coroutines 库。原因如下:
| 特性 | Kuikly 内建协程 | kotlinx.coroutines |
|---|---|---|
| 包大小 | 无额外依赖 | 增加约 1MB |
| 动态化支持 | ✅ 支持 | ❌ 不支持 |
| 线程安全 | 单线程,自动保障 | 需要开发者考虑 |
| 功能丰富度 | 基础功能 | 完整功能 |
Kuikly 协程的设计理念:
所有协程都在 Kuikly 线程中执行,没有线程切换,没有线程安全问题。
launch 函数源码解析
让我们逐行解析 launch 函数:
// 来自 Builders.kt
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.ATOMIC,
block: suspend CoroutineScope.() -> Unit
): Job {
// 1. 创建一个独立协程对象
val job = StandaloneCoroutine(context)
// 2. 启动协程
job.start(start, this) {
try {
// 3. 执行你传入的协程代码
block.invoke(this)
} catch (e: Throwable) {
// 4. 协程内的异常需要特殊处理
throwCoroutineScopeException(e)
}
}
// 5. 返回 Job 供外部控制
return job
}
参数详解:
-
context: CoroutineContext- 协程上下文,可以携带额外信息
- 默认是
EmptyCoroutineContext(空上下文)
-
start: CoroutineStart- 启动模式,Kuikly 只支持
ATOMIC(立即执行) - 标准库还有
LAZY(延迟启动)等模式
- 启动模式,Kuikly 只支持
-
block: suspend CoroutineScope.() -> Unit- 这是你要执行的协程代码
suspend:表示这是挂起函数CoroutineScope.():表示 Lambda 内部可以访问CoroutineScope的成员
CoroutineStart 的 invoke 操作符:
// 来自 CoroutineStart.kt
enum class CoroutineStart {
ATOMIC;
operator fun <R, T> invoke(
block: suspend R.() -> T,
receiver: R,
completion: Continuation<T>
): Unit =
when (this) {
ATOMIC -> block.startCoroutine(receiver, completion)
}
}
block.startCoroutine(receiver, completion) 是 Kotlin 标准库提供的函数,它:
- 创建协程的状态机
- 以
receiver作为接收者执行block - 执行完成后调用
completion.resumeWith(result)
async/await 机制解析
async 比 launch 多了返回值的处理:
fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.ATOMIC,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
// 使用 DeferredCoroutine 而不是 StandaloneCoroutine
val job = DeferredCoroutine<T>(context)
job.start(start, this) {
try {
block.invoke(this) // 返回值会被 DeferredCoroutine 捕获
} catch (e: Throwable) {
throwCoroutineScopeException(e)
val res: T? = null
res!! // 这行只是为了满足编译器,异常已经抛出了
}
}
return job
}
DeferredCoroutine 的实现(重点!):
internal class DeferredCoroutine<T>(
parentContext: CoroutineContext
) : AbstractCoroutine<T>(parentContext), Deferred<T> {
// 存储等待结果的回调列表
private var suspendCoroutineResumeTasks = fastArrayListOf<(T) -> Unit>()
// 标记是否已经有结果
private var didSetResultValue = false
// 存储结果值
private var resumeResultValue: T? = null
set(value) {
field = value
didSetResultValue = true
}
// await 的入口
override suspend fun await(): T = awaitInternal()
// 当协程完成时被调用(来自 Continuation 接口)
override fun resumeWith(result: Result<T>) {
if (result.isSuccess) {
// 保存结果
resumeResultValue = result.getOrNull()
// 通知所有等待者
suspendCoroutineResumeTasks.forEach { callback ->
callback.invoke(resumeResultValue as T)
}
suspendCoroutineResumeTasks.clear()
} else {
throw RuntimeException("result failure:" + result.exceptionOrNull())
}
}
// 内部实现:快速路径和慢速路径
private suspend fun awaitInternal(): T {
if (didSetResultValue) {
// 快速路径:结果已经有了,直接返回
return resumeResultValue as T
}
// 慢速路径:需要挂起等待
return awaitSuspend()
}
// 慢速路径:挂起当前协程,等待结果
private suspend fun awaitSuspend(): T = suspendCoroutine { continuation ->
// 把恢复协程的回调加入等待列表
this.suspendCoroutineResumeTasks.add { value ->
continuation.resume(value)
}
}
}
工作流程图解:
async { ... }
│
▼
┌─────────────────────┐
│ DeferredCoroutine │
│ 创建并开始执行 │
└─────────────────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
协程还在执行... 协程执行完成
│ │
│ ▼
│ resumeWith(result)
│ │
│ ▼
│ didSetResultValue = true
│ resumeResultValue = 结果值
│ │
▼ │
await() 被调用 │
│ │
▼ │
didSetResultValue? │
│ │
┌────┴────┐ │
│ │ │
true false │
│ │ │
▼ ▼ │
直接返回 awaitSuspend() │
结果值 │ │
▼ │
挂起,加入等待列表 ◄─────────────┘
│ 当结果可用时
▼ 通知所有等待者
恢复,返回结果值
delay 的实现原理
delay 函数让协程暂停指定时间:
suspend fun CoroutineScope.delay(timeMs: Int) {
// 1. 确定 pagerId(用于定时器管理)
val pagerId = if (this is LifecycleScope) {
pagerScope.pagerId
} else {
BridgeManager.currentPageId.ifEmpty { return }
}
// 2. 使用 suspendCoroutine 挂起协程
suspendCoroutine<Unit> { continuation ->
// 3. 设置定时器
setTimeout(pagerId, timeMs) {
try {
// 4. 定时器到期后恢复协程
continuation.resume(Unit)
} catch (e: Throwable) {
throwCoroutineScopeException(e)
}
}
}
}
核心要点:
suspendCoroutine挂起当前协程,拿到continuationsetTimeout设置原生定时器- 定时器回调中调用
continuation.resume(Unit)恢复协程
第四部分:suspendCoroutine 深度解析
回调地狱问题
假设我们有一个回调式的网络请求 API:
fun requestGet(
url: String,
params: JSONObject,
callback: (data: JSONObject, success: Boolean, error: String?) -> Unit
) {
// 原生实现...
}
如果需要顺序执行多个请求:
// 回调地狱 😱
requestGet(url1, params1) { data1, success1, error1 ->
if (success1) {
requestGet(url2, JSONObject().put("id", data1.getString("id"))) { data2, success2, error2 ->
if (success2) {
requestGet(url3, JSONObject().put("token", data2.getString("token"))) { data3, success3, error3 ->
if (success3) {
// 终于拿到最终数据了...
updateUI(data3)
} else {
showError(error3)
}
}
} else {
showError(error2)
}
}
} else {
showError(error1)
}
}
suspendCoroutine 的工作原理
suspendCoroutine 是桥接回调式 API 和协程的关键函数:
// Kotlin 标准库中的定义
public suspend inline fun <T> suspendCoroutine(
crossinline block: (Continuation<T>) -> Unit
): T
它做了什么?
- 挂起当前协程
- 把
Continuation对象传给你的block - 你在
block中决定何时调用continuation.resume(value)恢复协程
图解:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 协程执行中... │
│ │ │
│ ▼ │
│ 遇到 suspendCoroutine { continuation -> ... } │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ 协程挂起! │ │
│ │ 把 Continuation 交给你的 block │ │
│ └────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 你在 block 中做异步操作... │
│ (比如发起网络请求) │
│ │ │
│ ▼ │
│ 异步操作完成,调用 continuation.resume(result) │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ 协程恢复! │ │
│ │ suspendCoroutine 返回 result │ │
│ └────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 协程继续执行... │
│ │
└─────────────────────────────────────────────────────────────┘
实战:封装网络请求
将回调式 API 转换为挂起函数的标准模式:
// 原始的回调式 API
fun requestGet(
url: String,
params: JSONObject,
callback: (data: JSONObject, success: Boolean, errorMsg: String?, code: Int) -> Unit
)
// 封装为挂起函数
private suspend fun requestGet(url: String, params: JSONObject): JSONObject =
suspendCoroutine { continuation ->
// 调用原始 API
acquireModule<NetworkModule>(NetworkModule.MODULE_NAME).requestGet(
url,
params
) { data, success, errorMsg, _ ->
if (success) {
// 成功:恢复协程并返回数据
continuation.resume(data)
} else {
// 失败:恢复协程并抛出异常
continuation.resumeWithException(
IllegalStateException("请求失败: $errorMsg")
)
}
}
}
使用封装后的挂起函数:
// 现在可以像同步代码一样使用了!
lifecycleScope.launch {
try {
val user = requestGet("https://api.example.com/user", JSONObject())
val orders = requestGet(
"https://api.example.com/orders",
JSONObject().put("userId", user.getString("id"))
)
val detail = requestGet(
"https://api.example.com/order/detail",
JSONObject().put("orderId", orders.getJSONArray("list").getJSONObject(0).getString("id"))
)
// 更新 UI
orderDetail = detail
} catch (e: Exception) {
errorMessage = e.message ?: "未知错误"
}
}
通用封装模板:
/**
* 将任何回调式 API 转换为挂起函数的通用模板
* @param T 成功时的返回类型
*/
suspend fun <T> suspendCallback(
block: (onSuccess: (T) -> Unit, onError: (Exception) -> Unit) -> Unit
): T = suspendCoroutine { continuation ->
block(
// 成功回调
{ result -> continuation.resume(result) },
// 失败回调
{ error -> continuation.resumeWithException(error) }
)
}
// 使用模板
suspend fun fetchUserInfo(): User = suspendCallback { onSuccess, onError ->
userApi.getInfo(
onSuccess = { onSuccess(it) },
onError = { onError(it) }
)
}
第五部分:Kuikly 中的协程实战
GlobalScope vs LifecycleScope
Kuikly 提供了两种协程作用域:
1. GlobalScope(全局作用域)
object GlobalScope : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}
特点:
- 不与任何生命周期绑定
- 协程会一直运行直到完成或手动取消
- ⚠️ 使用不当可能导致内存泄漏
2. LifecycleScope(生命周期作用域)
class LifecycleScope(internal val pagerScope: PagerScope) : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}
// 在 Pager 中使用
abstract class Pager {
override val lifecycleScope: LifecycleScope by lazy(LazyThreadSafetyMode.NONE) {
LifecycleScope(this)
}
}
特点:
- 与页面生命周期绑定
- 页面销毁时自动清理资源
- ✅ 推荐在页面中使用
使用建议:
// ❌ 不推荐:GlobalScope 可能导致泄漏
GlobalScope.launch {
while (true) {
delay(1000)
updateUI() // 页面销毁后还在执行!
}
}
// ✅ 推荐:lifecycleScope 自动管理
lifecycleScope.launch {
while (true) {
delay(1000)
updateUI() // 页面销毁时自动停止
}
}
网络请求最佳实践
完整示例:
@Page("UserProfilePage")
internal class UserProfilePage : BasePager() {
// 响应式状态
private var isLoading by observable(false)
private var userData by observable<UserData?>(null)
private var errorMsg by observable<String?>(null)
override fun body(): ViewBuilder {
val ctx = this
return {
// ... UI 代码
vif({ ctx.isLoading }) {
LoadingView { }
}
vif({ ctx.userData != null }) {
UserInfoCard {
attr { data = ctx.userData }
}
}
vif({ ctx.errorMsg != null }) {
ErrorView {
attr { message = ctx.errorMsg }
event {
onRetry { ctx.fetchUserData() }
}
}
}
}
}
override fun created() {
super.created()
fetchUserData()
}
private fun fetchUserData() {
lifecycleScope.launch {
isLoading = true
errorMsg = null
try {
// 并行请求用户基本信息和统计数据
val userInfoDeferred = GlobalScope.async { fetchUserInfo() }
val statsDeferred = GlobalScope.async { fetchUserStats() }
// 等待两个请求都完成
val userInfo = userInfoDeferred.await()
val stats = statsDeferred.await()
// 合并数据
userData = UserData(userInfo, stats)
} catch (e: Exception) {
errorMsg = "加载失败: ${e.message}"
} finally {
isLoading = false
}
}
}
// 封装的挂起函数
private suspend fun fetchUserInfo(): JSONObject = suspendCoroutine { cont ->
acquireModule<NetworkModule>(NetworkModule.MODULE_NAME)
.requestGet("https://api.example.com/user", JSONObject()) { data, success, error, _ ->
if (success) cont.resume(data)
else cont.resumeWithException(Exception(error))
}
}
private suspend fun fetchUserStats(): JSONObject = suspendCoroutine { cont ->
acquireModule<NetworkModule>(NetworkModule.MODULE_NAME)
.requestGet("https://api.example.com/user/stats", JSONObject()) { data, success, error, _ ->
if (success) cont.resume(data)
else cont.resumeWithException(Exception(error))
}
}
}
定时任务与动画
实现定时器:
class Timer {
private var isRunning = false
private lateinit var action: () -> Unit
private var delay: Int = 0
private var period: Int = 0
fun schedule(delay: Int, period: Int, action: () -> Unit) {
this.delay = delay
this.period = period
this.action = action
start()
}
fun cancel() {
isRunning = false
}
private fun start() {
if (isRunning) return
isRunning = true
GlobalScope.launch {
delay(delay) // 初始延迟
while (isRunning) {
action()
delay(period) // 周期延迟
}
}
}
}
// 使用示例
val timer = Timer()
timer.schedule(delay = 0, period = 1000) {
count++ // 每秒增加
}
// 停止定时器
timer.cancel()
实现倒计时:
@Page("CountdownPage")
internal class CountdownPage : BasePager() {
private var seconds by observable(60)
override fun created() {
super.created()
lifecycleScope.launch {
while (seconds > 0) {
delay(1000)
seconds--
}
// 倒计时结束
onCountdownFinished()
}
}
}
实现流式文字效果:
@Page("TypewriterPage")
internal class TypewriterPage : BasePager() {
private var displayText by observable("")
private val fullText = "这是一段会逐字显示的文本..."
override fun created() {
super.created()
lifecycleScope.launch {
for (i in fullText.indices) {
displayText = fullText.substring(0, i + 1)
delay(50) // 每 50ms 显示一个字
}
}
}
}
多协程协作模式
模式 1:顺序执行
lifecycleScope.launch {
val step1Result = doStep1()
val step2Result = doStep2(step1Result)
val step3Result = doStep3(step2Result)
updateUI(step3Result)
}
模式 2:并行执行
lifecycleScope.launch {
// 同时启动多个异步任务
val deferred1 = GlobalScope.async { fetchData1() }
val deferred2 = GlobalScope.async { fetchData2() }
val deferred3 = GlobalScope.async { fetchData3() }
// 等待所有任务完成
val result1 = deferred1.await()
val result2 = deferred2.await()
val result3 = deferred3.await()
// 合并结果
updateUI(result1, result2, result3)
}
模式 3:多协程等待同一结果
// 只执行一次的初始化任务
private val initDeferred: Deferred<Config> by lazy {
GlobalScope.async {
delay(2000) // 模拟耗时初始化
loadConfig()
}
}
// 多个地方可以等待同一个结果
fun useConfig1() {
lifecycleScope.launch {
val config = initDeferred.await() // 等待初始化
doSomething1(config)
}
}
fun useConfig2() {
lifecycleScope.launch {
val config = initDeferred.await() // 复用同一个结果
doSomething2(config)
}
}
Kuikly 示例代码中的多协程等待:
@Page("CoroutineExamplePage")
internal class CoroutineExamplePage : BasePager() {
private var count by observable(0)
override fun created() {
super.created()
// 创建一个异步任务
val deferred = GlobalScope.async {
delay(3000) // 延迟 3 秒
}
// 多个协程等待同一个 deferred
GlobalScope.launch {
deferred.await()
count += 1 // 3 秒后执行
}
GlobalScope.launch {
deferred.await()
count += 1 // 同时执行
}
GlobalScope.launch {
deferred.await()
delay(1000) // 再等 1 秒
count += 1 // 4 秒后执行
}
// 最终效果:
// t=3s: count 变为 2
// t=4s: count 变为 3
}
}
第六部分:线程安全与最佳实践
Kuikly 的单线程模型
Kuikly 内建协程的一个重要特性是:所有协程都在 Kuikly 线程中执行。
这意味着:
- ✅ 不需要考虑线程同步
- ✅ 不需要使用锁
- ✅ 可以直接更新响应式状态
// 这样写是安全的!
lifecycleScope.launch {
val data = fetchData()
userData = data // 直接更新响应式属性,无需担心线程安全
}
线程安全验证机制
Kuikly 提供了验证机制来帮助发现问题:
@Page("DebugPage")
internal class DebugPage : BasePager() {
override fun willInit() {
super.willInit()
// 开启线程验证
Pager.VERIFY_THREAD = true
// 开启响应式观察者验证
Pager.VERIFY_REACTIVE_OBSERVER = true
// 自定义验证失败处理
Pager.verifyFailed { exception ->
// 记录日志
println("线程安全验证失败: ${exception.message}")
// 在调试模式下抛出异常
throw exception
}
}
}
最佳实践清单
1. 优先使用 lifecycleScope
// ✅ 推荐
lifecycleScope.launch { ... }
// ⚠️ 谨慎使用
GlobalScope.launch { ... }
2. 将回调式 API 封装为挂起函数
// ✅ 封装后
private suspend fun fetchData(): Data = suspendCoroutine { cont ->
api.fetch { data, success, error ->
if (success) cont.resume(data)
else cont.resumeWithException(Exception(error))
}
}
3. 使用 try-catch 处理异常
lifecycleScope.launch {
try {
val data = fetchData()
processData(data)
} catch (e: Exception) {
showError(e.message)
}
}
4. 并行请求提升性能
// ❌ 顺序执行(耗时长)
val data1 = fetch1() // 1s
val data2 = fetch2() // 1s
val data3 = fetch3() // 1s
// 总耗时 3s
// ✅ 并行执行(耗时短)
val d1 = async { fetch1() }
val d2 = async { fetch2() }
val d3 = async { fetch3() }
val data1 = d1.await()
val data2 = d2.await()
val data3 = d3.await()
// 总耗时 1s
5. 在 created() 中启动协程
override fun created() {
super.created()
// 在这里启动数据加载协程
lifecycleScope.launch {
loadData()
}
}
总结
核心概念回顾
| 概念 | 说明 |
|---|---|
| suspend | 标记函数可以挂起,是协程的基础 |
| Continuation | 协程的"书签",封装了恢复执行的能力 |
| CoroutineScope | 协程的作用域,管理协程生命周期 |
| Job | 协程的句柄,可用于取消协程 |
| Deferred | 带返回值的 Job,通过 await() 获取结果 |
| suspendCoroutine | 桥接回调 API 和协程的关键函数 |
Kuikly 协程特点
- 轻量级:不依赖 kotlinx.coroutines,包大小无额外增加
- 单线程:所有协程在 Kuikly 线程执行,无线程安全问题
- 支持动态化:可在动态化场景中使用
- API 兼容:提供
launch、async、delay、await等常用 API
使用建议
- 页面中优先使用
lifecycleScope:自动管理生命周期 - 封装回调为挂起函数:使用
suspendCoroutine消除回调地狱 - 并行请求提升性能:使用
async/await并行执行独立任务 - 正确处理异常:使用 try-catch 包裹协程代码
- 开启验证机制:在开发阶段使用
VERIFY_THREAD发现问题