Skip to content

IntelliJ IDEA を使用して Kotlin Flow をデバッグする – チュートリアル

このチュートリアルでは、IntelliJ IDEA を使用して Kotlin Flow を作成しデバッグする方法を説明します。

このチュートリアルは、コルーチンKotlin Flowの概念に関する事前知識があることを前提としています。

Kotlin Flow を作成する

低速なエミッターと低速なコレクターを持つ Kotlin Flow を作成します。

  1. IntelliJ IDEA で Kotlin プロジェクトを開きます。プロジェクトがない場合は、作成してください。

  2. Gradle プロジェクトで kotlinx.coroutines ライブラリを使用するには、build.gradle(.kts) に次の依存関係を追加します。

    kotlin
    dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
    }
    groovy
    dependencies {
        implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2'
    }

    他のビルドシステムについては、kotlinx.coroutines README の指示を参照してください。

  3. src/main/kotlin にある Main.kt ファイルを開きます。

    src ディレクトリには Kotlin のソースファイルとリソースが含まれています。Main.kt ファイルには、Hello World! を出力するサンプルコードが含まれています。

  4. 3つの数値の Flow を返す simple() 関数を作成します。

    • CPU を消費するブロッキングコードを模倣するために、delay() 関数を使用します。この関数は、スレッドをブロックすることなくコルーチンを 100 ミリ秒間サスペンドします。
    • for ループ内でemit() 関数を使用して値を生成します。
    kotlin
    import kotlinx.coroutines.*
    import kotlinx.coroutines.flow.*
    import kotlin.system.*
    
    fun simple(): Flow<Int> = flow {
        for (i in 1..3) {
            delay(100)
            emit(i)
        }
    }
  5. main() 関数内のコードを変更します。

    • コルーチンをラップするために、runBlocking() ブロックを使用します。
    • collect() 関数を使用して発行された値を収集します。
    • CPU を消費するコードを模倣するために、delay() 関数を使用します。この関数は、スレッドをブロックすることなくコルーチンを 300 ミリ秒間サスペンドします。
    • println() 関数を使用して、Flow から収集された値を出力します。
    kotlin
    fun main() = runBlocking {
        simple()
            .collect { value ->
                delay(300)
                println(value)
            }
    }
  6. Build Project をクリックしてコードをビルドします。

    Build an application

コルーチンをデバッグする

  1. emit() 関数が呼び出されている行にブレークポイントを設定します。

    Build a console application

  2. 画面上部の実行構成の隣にある Debug をクリックして、コードをデバッグモードで実行します。

    Build a console application

    Debug ツールウィンドウが表示されます。

    • Frames タブにはコールスタックが含まれています。
    • Variables タブには現在のコンテキストの変数が含まれています。Flow が最初の値を発行していることがわかります。
    • Coroutines タブには、実行中またはサスペンドされたコルーチンに関する情報が含まれています。

    Debug the coroutine

  3. Debug ツールウィンドウで Resume Program をクリックしてデバッガーセッションを再開します。プログラムは同じブレークポイントで停止します。

    Debug the coroutine

    これで Flow は2番目の値を発行します。

    Debug the coroutine

最適化により除外された変数

suspend 関数を使用している場合、デバッガーで変数の名前の隣に「was optimized out」というテキストが表示されることがあります。

Variable "a" was optimized out

このテキストは、変数のライフタイムが短縮され、その変数がもはや存在しないことを意味します。 値が見えないため、最適化された変数を含むコードをデバッグするのは困難です。 -Xdebug コンパイラオプションを使用すると、この動作を無効にできます。

本番環境ではこのフラグを絶対に使用しないでください : -Xdebugメモリリークを引き起こす可能性があります。

並行して実行されるコルーチンを追加する

  1. src/main/kotlin にある Main.kt ファイルを開きます。

  2. エミッターとコレクターを並行して実行するようにコードを強化します。

    • エミッターとコレクターを並行して実行するために、buffer() 関数への呼び出しを追加します。buffer() は発行された値を格納し、Flow コレクターを別のコルーチンで実行します。
    kotlin
    fun main() = runBlocking<Unit> {
        simple()
            .buffer()
            .collect { value ->
                delay(300)
                println(value)
            }
    }
  3. Build Project をクリックしてコードをビルドします。

2つのコルーチンで Kotlin Flow をデバッグする

  1. println(value) に新しいブレークポイントを設定します。

  2. 画面上部の実行構成の隣にある Debug をクリックして、コードをデバッグモードで実行します。

    Build a console application

    Debug ツールウィンドウが表示されます。

    Coroutines タブでは、2つのコルーチンが並行して実行されていることがわかります。buffer() 関数により、Flow コレクターとエミッターは別のコルーチンで実行されます。 buffer() 関数は Flow から発行された値をバッファリングします。 エミッターコルーチンは RUNNING ステータスになっており、コレクターコルーチンは SUSPENDED ステータスになっています。

  3. Debug ツールウィンドウで Resume Program をクリックして、デバッガーセッションを再開します。

    Debugging coroutines

    これでコレクターコルーチンは RUNNING ステータスになり、一方エミッターコルーチンは SUSPENDED ステータスになっています。

    各コルーチンをさらに詳しく調べて、コードをデバッグできます。