Android中的Kt協(xié)程Coroutine

定義

協(xié)程(Coroutine)是Kotlin提供的一種輕量級線程,用于簡化異步編程。它可以在單線程內(nèi)實現(xiàn)并發(fā)操作,通過掛起(suspend)恢復(fù)(resume)機制,讓異步代碼看來像同步代碼一樣直觀。

協(xié)程的核心是掛起函數(shù)(suspend),它可以在不阻塞當(dāng)前線程的情況下等待操作完成,完成后自動恢復(fù)執(zhí)行。

特點

  1. 輕量級

    協(xié)程運行在用戶態(tài),不直接依賴操作系統(tǒng)線程,可以在一個線程中運行成千上萬個協(xié)程,開銷極小。

  2. 結(jié)構(gòu)化并發(fā)

    協(xié)程通過作用域(CoroutineScope)管理生命周期,確保所有字協(xié)程在父協(xié)程完成前完成,避免資源泄露。

  3. 取消機制

    協(xié)程支持協(xié)作取消,可以隨時取消不再需要的任務(wù),釋放資源。

  4. 異步處理簡單

    使用常規(guī)的try-catch即可捕捉協(xié)程內(nèi)部異常,或者通過CoroutineExceptionHandler統(tǒng)一處理。

  5. 與Android生命周期集成

    官方提供了lifecycleScope(Activity/Fragment)和viewModelScope(ViewModel),自動感知生命周期,在銷毀時自動取消協(xié)程。

  6. 調(diào)度器(Dispatcher)

    協(xié)程可以靈活切換線程:Dispathchers.Main(主線程)、Dispatchers.IO(IO密集型任務(wù))、Disapatchers.Default(CPU密集型任務(wù))等。

Kotlin 協(xié)程作用域

Kotlin 協(xié)程主要有以下幾種作用域構(gòu)建器:

GlobalScope(全局作用域)

  • 生命周期:應(yīng)用程序整個生命周期
  • 使用場景:不推薦使用,僅適用于與應(yīng)用程序生命周期的相同任務(wù)
GlobalScope.launch {
    // 不推薦
}
特點:
  1. 不需要在任何范圍內(nèi),即可啟動對象
  2. 容易導(dǎo)致內(nèi)存泄漏
  3. 無法自動取消
  4. 以及被標(biāo)記@DelicateCoroutinesApi??

lifecycleScope(生命周期作用域)

  • 生命周期:與Lifecycle綁定(Activity/Fragment)
  • 使用場景:UI相關(guān)的協(xié)程操作
lifecycleScope.launch {
    // 推薦用于 Activity/Fragment
}
特點:
  1. 自動管理生命周期
  2. Lidecycle銷毀時自動取消
  3. 需要引入 lifecycle-runtime-ktx
  4. 推薦使用?

viewModeScope(ViewModel 作用域)

  • 生命周期:與ViewModel綁定
  • 使用場景:在ViewModel中執(zhí)行后臺任務(wù)
viewModelScope.launch {
    // 推薦用于 ViewModel
}
特點:
  1. ViewModel清除時候自動取消
  2. 需要引入lifecycle-viewmodel-ktx
  3. 推薦使用?

coroutineScope(協(xié)程作用域)

  • 生命周期:等待所有的子協(xié)程完成
  • 使用場景:需要等待所有的子任務(wù)完成的場景
coroutineScope {
    launch { /* 任務(wù) 1 */ }
    launch { /* 任務(wù) 2 */ }
    // 等待所有任務(wù)完成
}
特點:
  1. 會阻塞當(dāng)前協(xié)程直到所有的子協(xié)程完成場景
  2. 失敗會傳播異常,一個失敗就會拋出異常一損俱損
  3. 結(jié)構(gòu)化并發(fā)

SupervisorScop(監(jiān)督作用域)

  • 生命周期:等待所有的子協(xié)程完成
  • 使用場景:子任務(wù)相互獨立,一個失敗不影響其他任務(wù)
supervisorScope {
    launch { 
        // 失敗不會影響其他協(xié)程
    }
    launch {
        // 繼續(xù)執(zhí)行
    }
}
特點:
  1. 子協(xié)程失敗不會導(dǎo)致其他協(xié)程取消,獨立失敗,互不影響
  2. 需要手動處理異常
  3. 更靈活的錯誤處理

CoroutineScope(自定義作用域)

  • 生命周期:手動控制
  • 使用場景:自定義生命周期管理
class MyRepository {
    val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    
    fun doWork() {
        scope.launch {
            // 后臺工作
        }
    }
    
    fun cleanup() {
        scope.cancel()
    }
}
特點:
  1. 需要手動創(chuàng)建和取消
  2. 靈活性更高
  3. 需要謹(jǐn)慎管理避免內(nèi)存泄漏

協(xié)程作用域?qū)φ毡?/h4>
作用域 生命周期 自動取消 異常傳播 使用場景 推薦度
GlobalScope 應(yīng)用全程 ? ? 不推薦使用 ?? 不推薦
lifecycleScope Lifecycle ? ? UI 層 (Activity/Fragment) ?????
viewModelScope ViewModel ? ? ViewModel 層 ?????
coroutineScope 子協(xié)程完成 ? ? 需要等待所有子任務(wù) ????
supervisorScope 子協(xié)程完成 ? ? 子任務(wù)相互獨立,互不影響 ????
CoroutineScope 手動控制 ? 可配置 自定義場景 ???

Kotlin 協(xié)程啟動器:launch、async、runBlocking 詳解

Launch

//源代碼
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
}
特點
  • 返回類型:Job 【不返回結(jié)果】
  • 使用場景:“發(fā)射并忘記”,不需要返回結(jié)果的異步操作
  • 異常處理:異常會被傳播到 CoroutineExceptionHandler或者 父協(xié)程
參考案例:
// 在已有的協(xié)程作用域中
val job = coroutineScope.launch {
    delay(1000)
    println("執(zhí)行后臺任務(wù)")
}

// 可以取消任務(wù),有內(nèi)鬼終止交易
job.cancel()

async

//源代碼
public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    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<T> 可等待的泛型結(jié)果
  • 使用場景:需要返回結(jié)果的異步操作,可并行執(zhí)行多個任務(wù)
  • 異常處理:異常會在調(diào)用wawit()時候拋出
參考案例:
val scope = CoroutineScope(Dispatchers.IO + Job())

// 并行執(zhí)行多個任務(wù)
val deferred1 = scope.async { fetchData1() }
val deferred2 = scope.async { fetchData2() }

// 等待結(jié)果
val result1 = deferred1.await()
val result2 = deferred2.await()

// 或使用 awaitAll 并行等待
val results = listOf(deferred1, deferred2).awaitAll()

runBlocking

//源代碼
public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    val currentThread = Thread.currentThread()
    val contextInterceptor = context[ContinuationInterceptor]
    val eventLoop: EventLoop?
    val newContext: CoroutineContext
    if (contextInterceptor == null) {
        // create or use private event loop if no dispatcher is specified
        eventLoop = ThreadLocalEventLoop.eventLoop
        newContext = GlobalScope.newCoroutineContext(context + eventLoop)
    } else {
        // See if context's interceptor is an event loop that we shall use (to support TestContext)
        // or take an existing thread-local event loop if present to avoid blocking it (but don't create one)
        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()
}
特點
  • 返回類型:T 阻塞當(dāng)前線程直到完成
  • 使用場景:橋接同步和異步代碼,測試環(huán)境
  • 異常處理:直接拋出異常
參考案例
// 阻塞主線程
fun main() {
    runBlocking {
        val result = async { loadData() }.await()
        println(result)
    }
    // 上面的代碼會阻塞直到協(xié)程完成
}

// 測試中使用
@Test
fun testCoroutine() = runBlocking {
    val result = repository.getData()
    assertEquals("expected", result)
}

對比總結(jié)表 launch、async、runBlocking

啟動器 launch async runBlocking
返回值 Job Deferred<T> T
是否阻塞
獲取結(jié)果 ? ? await() ? 直接返回
使用場景 不關(guān)心結(jié)果 需要結(jié)果 測試/橋接
UI 線程可用 ? ? ?
性能開銷 ??? ?? ?

suspend 掛起狀態(tài)

一個掛起任務(wù)runSuspendTalks

suspend fun runSuspendTalks(): String {
    log("協(xié)程掛起任務(wù) runSuspendTalks ")
    delay(1000 * 3)
    return "100"
}

反編譯成Java語音查看區(qū)別

// 方法簽名添加了 Continuation 參數(shù)
@Nullable
public final String runSuspendTalks(@NotNull Continuation<? super runSuspendTalks> continuation) {
    // 檢查 Continuation 是否是特定狀態(tài)機的實例
    if (continuation instanceof runSuspendTalks$CoroutineImpl) {
        runSuspendTalks$CoroutineImpl coroutineImpl = (runSuspendTalks$CoroutineImpl)continuation;
        int label = coroutineImpl.label;
        coroutineImpl.label = 0;
        
        // 根據(jù)狀態(tài)執(zhí)行不同的代碼段
        if (label == 0) {
            // 正常執(zhí)行邏輯
            this.log("協(xié)程掛起任務(wù) runSuspendTalks ");
            
            // 調(diào)用 delay 時會掛起
            Object result = DelayKt.delay(3000L, coroutineImpl);
            if (result == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
                return result; // 掛起,返回掛起點
            }
            
            // 恢復(fù)執(zhí)行
            return "100";
        } else if (label == 1) {
            // 從 delay 掛起點恢復(fù)
            // ... 繼續(xù)執(zhí)行后續(xù)代碼
            return "100";
        }
    }
    
    // 創(chuàng)建新的狀態(tài)機實例
    runSuspendTalks$CoroutineImpl impl = new runSuspendTalks$CoroutineImpl(this, continuation);
    return impl.invokeSuspend(Unit.INSTANCE);
}

==============================  生成的狀態(tài)機類(偽代碼): ===========================================

// 編譯器生成的狀態(tài)機類
final class runSuspendTalks$CoroutineImpl extends CoroutineImpl {
    int label;
    Object L$0; // 局部變量存儲
    
    public final Object invokeSuspend(Object result) {
        // 狀態(tài)機核心邏輯
        switch(label) {
            case 0:
                // 第一次執(zhí)行
                label = 1;
                this.result = result;
                break;
            case 1:
                // 從掛起恢復(fù)
                break;
        }
        return result;
    }
}
關(guān)鍵點
  1. Continuation參數(shù):每個suspend函數(shù)都會多出一個Continuation<T>參數(shù),用于回調(diào)恢復(fù)
  2. 狀態(tài)機:編譯器將suspend函數(shù)轉(zhuǎn)換成有限狀態(tài)機,通過label標(biāo)記執(zhí)行位置
  3. 掛起點:每次調(diào)用其他suspend函數(shù)(如:delay)時:
    • 保存當(dāng)前狀態(tài)到Continuation
    • 返回特殊值 COROUTINE_SUSPENDED
    • 異步操作完成后,通過Continuation.resume()恢復(fù)
  4. 局部變量提示:suspend函數(shù)中的局部變量會提升狀態(tài)機的字段

withContent調(diào)度器

可用withContent切換協(xié)程上下文的線程,如 I/O 操作、CPU 密集型任務(wù)等

suspend fun fetchUserData(): String {
    // 在 IO 線程執(zhí)行網(wǎng)絡(luò)請求或數(shù)據(jù)庫操作
    val userData = withContext(Dispatchers.IO) {
        // 執(zhí)行耗時的 I/O 操作
        performNetworkRequest()
    }
    
    // 自動返回到原始上下文(可能是主線程)
    // 更新 UI
    updateUI(userData)
    
    return userData
}
  • Dispatchers.Main:Android 主線程,用于更新 UI
  • Dispatchers.IO:適用于 I/O 密集型任務(wù),如網(wǎng)絡(luò)請求、文件讀寫
  • Dispatchers.Default:適用于 CPU 密集型任務(wù),例如算法,非I/O操作流
  • Dispatchers.Unconfined:無限制調(diào)度器,很少使用

cancel 取消協(xié)程

  • Job.cancel()
val job = launch {
    // 協(xié)程任務(wù)
}
job.cancel()  // 取消協(xié)程
  • async 返回的是 Deferred<T>(繼承自 Job),取消方式類似 deferred.cancel()
val deferred = scope.async {}
// 取消 async 協(xié)程
deferred.cancel()
  • 取消整個 CoroutineScope,及父作用域
val scope = CoroutineScope(Dispatchers.IO + Job())
scope.launch { /* ... */ }
scope.coroutineContext[Job]?.cancel()  // 取消作用域內(nèi)所有協(xié)程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容