Kotlinのコルーチンを完全に理解するための記事があります

バックグラウンド

非同期スレッドによって生成されたコールバック地獄を解決するために

//传统回调方式
api.login(phone,psd).enquene(new Callback<User>(){
  public void onSuccess(User user){
    api.submitAddress(address).enquene(new Callback<Result>(){
      public void onSuccess(Result result){
        ...
      }
    });
  }
});
//使用协程后
val user=api.login(phone,psd)
api.submitAddress(address)
...

コルーチンとは

基本的に、コルーチンは軽量のスレッドです。

コルーチンの主要名詞

val job = GlobalScope.launch {
    delay(1000)
    println("World World!")
}
  • CoroutineScope(アクションの範囲)

    GlobeScope、lifecycleScope、viewModelScope、その他のカスタムCoroutineScopeを含むコルーチンコードブロックの実行スレッドとライフサイクルを制御します

    GlobeScope:グローバルスコープ、実行を自動的に終了しません

    lifecycleScope:ライフサイクルスコープ。アクティビティおよびその他のライフサイクルコンポーネントに使用されます。破棄されると自動的に終了し、追加の導入が必要です。

    viewModelScope:ViewModelで使用されるviewModelスコープは、ViewModelがリサイクルされると自動的に終了します。追加の導入が必要です

  • ジョブ

    コルーチンの測定単位。これはジョブタスクに相当します。launchメソッドはデフォルトで新しいジョブを返します。

  • サスペンド

    メソッドに作用するということは、上記の遅延メソッドのように、メソッドが時間のかかるタスクであることを意味します。

public suspend fun delay(timeMillis: Long) {
    ...
}

コルーチンの導入

メインフレーム($ coroutines_versionは、以下と同じ1.3.9などの最新バージョンに置き換えられます)

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"

lifecycleScope(オプション、バージョン2.2.0)

implementation 'androidx.activity:activity-ktx:$lifecycle_scope_version'

viewModelScope(オプション、バージョン2.3.0-beta01)

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$coroutines_viewmodel_version"

使いやすい

簡単な例を挙げましょう

lifecycleScope.launch { 
    delay(2000)
    tvTest.text="Test"
}

上記の例で実装された関数は、2秒間待機してから、IDがtvTestであるTextViewコントロールのテキスト値をTestに変更することです。

カスタム遅延リターン方式

Kotlinでは、結果を返すために遅延が必要なメソッドの場合、サスペンドを使用して示す必要があります

lifecycleScope.launch {
    val text=getText()
    tvTest.text = text
}
suspend fun getText():String{
    delay(2000)
    return "getText"
}

他のスレッドのスレッド切り替えにContinuationを使用する必要がある場合は、suspendCancellableCoroutineまたはsuspendCoroutineを使用してラップし(前者はキャンセルできます。これは後者の拡張と同等です)、it.resume()を正常に呼び出して呼び出します。失敗した場合はresumeWithException(Exception())、例外をスローします

suspend fun getTextInOtherThread() = suspendCancellableCoroutine<String> {
    thread {
        Thread.sleep(2000)
        it.resume("getText")
    }
}

例外キャッチ

コルーチンの失敗は、特別な状況に均一に対処するための例外によって捕捉される可能性があります

lifecycleScope.launch {
    try {
        val text=getText()
        tvTest.text = text
    } catch (e:Exception){
        e.printStackTrace()
    }
}

キャンセル機能

以下の2つのジョブが実行されます。最初のジョブはオリジナルで、2番目のジョブは1秒後に最初のジョブをキャンセルします。これにより、tvTextのテキストは変更されません。

val job = lifecycleScope.launch {
    try {
        val text=getText()
        tvTest.text = text
    } catch (e:Exception){
        e.printStackTrace()
    }
}
lifecycleScope.launch {
    delay(1000)
    job.cancel()
}

タイムアウトを設定する

これは、withTimeout関数に対応する自動キャンセル関数をカプセル化するシステムと同等です。

lifecycleScope.launch {
    try {
        withTimeout(1000) {
            val text = getText()
            tvTest.text = text
        }
    } catch (e:Exception){
        e.printStackTrace()
    }
}

戻り値のあるジョブ

起動と同様に、Jobの拡張クラスに属するDeferredオブジェクトを返すasyncメソッドもあります。Deferredは返された結果を取得できます。具体的な使用法は次のとおりです。

lifecycleScope.launch {
    val one= async {
        delay(1000)
        return@async 1
    }
    val two= async {
        delay(2000)
        return@async 2
    }
    Log.i("scope test",(one.await()+two.await()).toString())
}

高度な高度な

カスタムCoroutineScope

最初にCoroutineScopeソースコードを見てください

public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}

CoroutineScopeには主にcoroutineContextオブジェクトが含まれています。カスタマイズするには、coroutineContextのgetメソッドを実装するだけで済みます。

class TestScope() : CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = TODO("Not yet implemented")
}

coroutineContextを作成するには、最初にCoroutineContextが何であるかを知る必要があります。次に、CoroutineContextのソースコードを確認します。

/**
 * Persistent context for the coroutine. It is an indexed set of [Element] instances.
 * An indexed set is a mix between a set and a map.
 * Every element in this set has a unique [Key].
 */
public interface CoroutineContext {
    public operator fun <E : Element> get(key: Key<E>): E?
    public fun <R> fold(initial: R, operation: (R, Element) -> R): R
    public operator fun plus(context: CoroutineContext): CoroutineContext = 
        ...
    public fun minusKey(key: Key<*>): CoroutineContext

    public interface Key<E : Element>
    public interface Element : CoroutineContext {
        ...
    }
}

コメントを通じて、それは本質的にElementを含むコレクションであることがわかりますが、setおよびmapコレクションとは異なり、取得(get)、折りたたみ(fold、加算と置換の組み合わせ)、および減算(minusKey、shift)を実装します。 )、オブジェクトの組み合わせ(さらに、val coroutineContext = coroutineContext1 + coroutineContext2など)

その主な内容はElementであり、Elementの実現には

  • ジョブタスク
  • ContinuationInterceptorインターセプター
  • AbstractCoroutineContextElement
  • CoroutineExceptionHandler
  • ThreadContextElement
  • DownstreamExceptionElement

Elementは多くの場所で実装されており、その主な目的はスコープを制限して例外を処理することであることがわかります。ここでは、最初に2つの重要な要素を理解します。1つはジョブで、もう1つはCoroutineDispatcherです。

ジョブ
  • ジョブ:子ジョブがキャンセルされると、親ジョブと他の子ジョブがキャンセルされます。親ジョブがキャンセルされると、すべての子ジョブがキャンセルされます。
  • SupervisorJob:親ジョブがキャンセルされ、すべての子ジョブがキャンセルされます
CoroutineDispatcher
  • Dispatchers.Main:メインスレッドの実行
  • Dispatchers.IO:IOスレッドの実行

lifecycleScopeと同様のカスタムTestScopeをシミュレートします

class TestScope() : CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = SupervisorJob() +Dispatchers.Main
}

ここでは、合計プロセスラインSupervisorJob()と特定の実行環境Dispatchers.Main(Androidメインスレッド)を定義します。アクティビティのlifecycleScopeを置き換える場合は、アクティビティにインスタンスを作成する必要があります。

val testScope=TestScope()

その後、アクティビティが破棄されたら、すべてのジョブをキャンセルします

override fun onDestroy() {
    testScope.cancel()
    super.onDestroy()
}

その他の使用方法は、lifecycleScopeと同じです。

testScope.launch{
    val text = getText()
    tvTest.text = text
}

仕事の深い理解

CoroutineScopeにはメインジョブが含まれ、launchまたは後で呼び出される他のメソッドによって作成されたジョブは、CoroutineScopeの子ジョブに属します。各ジョブには、isActive、isCompleted、isCancelled、およびいくつかの基本操作start()、cancelなどの独自の状態があります。 ()、join()、具体的な変換プロセスは次のとおりです

ジョブステータスdiagram.png

ジョブの作成から始めましょう。launchが呼び出されると、デフォルトでCoroutineContext、CoroutineStart、およびコードブロックの3つのパラメーターがあります。

  • コンテキスト:CoroutineContextのオブジェクト。デフォルトはCoroutineStart.DEFAULTで、CoroutineScopeのコンテキストで折りたたまれます。
  • start:CoroutineStartのオブジェクト。デフォルトはCoroutineStart.DEFAULTで、即時実行を表します。CoroutineStart.LAZYは、非即時実行を表し、ジョブのstart()を呼び出して実行を開始する必要があります。
val job2= lifecycleScope.launch(start =  CoroutineStart.LAZY) {
    delay(2000)
    Log.i("scope test","lazy")
}
job2.start()

このモードで作成された場合、デフォルトは新しい状態です。この時点で、isActive、isCompleted、およびisCancelledはすべてfalseです。startが呼び出されると、アクティブ状態に変換されます。isActiveのみがtrueです。タスクが完了した場合、完了状態になります。このとき、子ジョブの完了を待機しています。この状態では、isActiveのみがtrueです。すべての子ジョブも完了している場合は、Completed状態になり、isCompletedのみになります。本当です。アクティブ状態または完了状態でキャンセルまたは例外が発生すると、キャンセル状態になります。親ジョブおよび他の子ジョブをキャンセルする必要がある場合は、キャンセルが完了するのを待ちます。現時点では、isCancelledのみです。キャンセルが完了すると、最終的にキャンセル状態になり、isCancelledとisCompletedの両方がtrueになります。

状態 アクティブです 完成されました isCancelled
新着 FALSE FALSE FALSE
アクティブ TRUE FALSE FALSE
完了 TRUE FALSE FALSE
キャンセル FALSE FALSE TRUE
キャンセル FALSE TRUE TRUE
完了 FALSE TRUE FALSE

さまざまなジョブの相互作用では、join()とcancelAndJoin()を使用する必要があります

  • join():現在のジョブを他のコルーチンタスクに追加します
  • cancelAndJoin():操作をキャンセルし、追加してからキャンセルします
val job1= GlobleScope.launch(start =  CoroutineStart.LAZY) {
    delay(2000)
    Log.i("scope test","job1")
}
lifecycleScope.launch {
    job1.join()
    delay(2000)
    Log.i("scope test","job2")
}

サスペンドの深い理解

kotlinの新しいメソッド修飾子として一時停止します。最終的な実装はまだJavaです。違いを見てみましょう。

suspend fun test1(){}
fun test2(){}

Javaコードに対応

public final Object test1(@NotNull Continuation $completion) {
  return Unit.INSTANCE;
}
public final void test2() {
}

対応するバイトコード

public final test1(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
  ...
   L0
    LINENUMBER 6 L0
    GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/lieni/android_c/ui/test/TestActivity; L0 L1 0
    LOCALVARIABLE $completion Lkotlin/coroutines/Continuation; L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2

public final test2()V
   L0
    LINENUMBER 9 L0
    RETURN
   L1
    LOCALVARIABLE this Lcom/lieni/android_c/ui/test/TestActivity; L0 L1 0
    MAXSTACK = 0
    MAXLOCALS = 1

ご覧のとおり、サスペンドを使用するメソッドは、渡されたときに追加のContinuationオブジェクトがあり、Unit.INSTANCEオブジェクトが返されることを除いて、実際には通常のメソッドと同じです。

Continuationは、コンテキストオブジェクトとresumeWithメソッドを含むインターフェイスです。

public interface Continuation<in T> {
    public val context: CoroutineContext
    public fun resumeWith(result: Result<T>)
}

Continuationの具体的な実装は、BaseContinuationImplにあります。

internal abstract class BaseContinuationImpl(...) : Continuation<Any?>, CoroutineStackFrame, Serializable {
    public final override fun resumeWith(result: Result<Any?>) {
        ...
        while (true) {
            ...
            with(current) {
              	val outcome = invokeSuspend(param)
                ...
                releaseIntercepted() 
                if (completion is BaseContinuationImpl) {
                    ...
                } else {
                    ...
                    return
                }
            }
        }
    }
    ...
}

resumeWithを呼び出すと、ループを実行し続け、最上位の完了が完了して戻るまで、invokeSuspend(param)とreleaseIntercepted()を呼び出し、コルーチンのインターセプターを解放します。

最終リリースはContinuationImplに実装されています

internal abstract class ContinuationImpl(...) : BaseContinuationImpl(completion) {
    ...
    protected override fun releaseIntercepted() {
        val intercepted = intercepted
        if (intercepted != null && intercepted !== this) {
            context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
        }
        this.intercepted = CompletedContinuation 
    }
}

ここを通じて、リリースは最終的にCoroutineContextのContinuationInterceptorの要素を通じて実現されます。

一時停止についても同じことが言えます。引き続きsuspendCoroutineを確認してください。

public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =
    suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
        val safe = SafeContinuation(c.intercepted())
        ...
    }

Continuationのintercepted()メソッドはデフォルトで呼び出されます

internal abstract class ContinuationImpl(...) : BaseContinuationImpl(completion) {
    ...
    public fun intercepted(): Continuation<Any?> =intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }
}

一時停止は、CoroutineContextのContinuationInterceptorである要素によって最終的に実現されることがわかります。

プロセスの概要(スレッドの切り替え)

  • 新しい継続を作成する
  • CoroutineScopeでコンテキストのContinuationInterceptorのinterceptContinuationメソッドを呼び出して、親タスクを一時停止します
  • サブタスクの実行(スレッドが指定されている場合、そのスレッドは新しいスレッドで実行され、継続オブジェクトが渡されます)
  • 実行が完了した後、ユーザーはresumeまたはresumeWith of Continuationを呼び出して、結果を返します。
  • CoroutineScopeでコンテキストのContinuationInterceptorのreleaseInterceptedContinuationメソッドを呼び出して、親タスクを復元します

ブロッキングと非ブロッキング

CoroutineScopeは、デフォルトでは現在のスレッドをブロックしません。ブロックする必要がある場合は、runBlockingを使用できます。メインスレッドで次のコードを実行すると、2秒の白い画面が表示されます。

runBlocking { 
    delay(2000)
    Log.i("scope test","runBlocking is completed")
}

ブロッキングの原則:runBlockingを実行すると、デフォルトでBlockingCoroutineが作成され、BlockingCoroutineは、現在のジョブがisCompleted状態になるまでループを実行し続けます。

public fun <T> runBlocking(...): T {
    ...
    val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
    coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
    return coroutine.joinBlocking()
}
private class BlockingCoroutine<T>(...) : AbstractCoroutine<T>(parentContext, true) {
    ...
    fun joinBlocking(): T {
      ...
      while (true) {
        ...
        if (isCompleted) break
        ...
      }    
      ...
    }
}

Googleの高度なKotlin拡張実用マニュアル(デモ付き)のコピーをあなたと共有しましょう。

第1章Kotlinの概要

  • Kotlinの概要
  • KotlinとJavaの比較
  • AndroidStudioを上手に使う
  • Kotlinの基本的なタイプを知る
  • Kotlinの配列に入る
  • Kotlinコレクションにアクセスします
  • 収集の問題
  • 完全なコード
  • 基本的な文法

第2章落とし穴を回避するためのKotlin実用ガイド

  • メソッド入力パラメーターは定数であり、変更できません
  • コンパニオンなし、インスタンス?
  • Javaのオーバーロード、Kotlinでどのように巧妙な移行を行いますか?
  • Kotlinの空の姿勢
  • KotlinはJava親クラスのメソッドを上書きします
  • Kotlinは「冷酷」になり、TODOでさえ手放しません!
  • ピットインは、 `として
  • Kotlinのプロパティの理解
  • キーワードも
  • takeIfキーワード
  • takeIfキーワード
  • シングルトンモード

第3章プロジェクト実際の戦闘「KotlinJetpack実際の戦闘」

  • 偉大な神を崇拝するデモから始まります
  • KotlinがGradleスクリプトを作成するのはどのような経験ですか?
  • Kotlinプログラミングのトリプルレルム
  • Kotlinの高階関数
  • Kotlinジェネリック
  • Kotlin拡張機能
  • Kotlinコミッション
  • コルーチンの「不明な」デバッグスキル
  • グラフィカルコルーチン:サスペンド

このPDFドキュメントが必要な友達は、こちらのコミュニケーションスカートに参加できます。フロント:1102、ミドル:405、最後:044。スカートには学生や大物がいて、リソースは自由に共有できます。

おすすめ

転載: blog.csdn.net/zhireshini233/article/details/114854185