KotlinのCoroutineScopeとcoroutineScopeの違い

David Mbochi Njonge 2024年2月15日
  1. 新しい Kotlin プロジェクトを作成する
  2. メイン レイアウトにボタンを作成する
  3. Kotlin で CoroutineScope を使用する
  4. Kotlin で coroutineScope を使用する
  5. まとめ
KotlinのCoroutineScopeとcoroutineScopeの違い

Kotlin のドキュメントでは、コルーチン を、ネットワークからのリソースのダウンロードなど、一部のバックグラウンド タスクの実行を待機しているときに一時停止できる実行として定義しています。

コルーチンは、コルーチンが中断されたときに他の計算が実行され続けるため、同時実行を実現するのに役立ちます。 コルーチンは使用しているスレッドから独立していることに注意してください。これは、使用していたスレッドで実行が再開されることが保証されていないためです。

新しいコルーチンを作成するには、スコープ内で行う必要があります。 このチュートリアルでは、構造化されていない同時実行のスコープが子コルーチンにどのように影響するか、および構造化された同時実行を使用してスコープの問題を解決する方法を学習します。

新しい Kotlin プロジェクトを作成する

このチュートリアルでは IntelliJ IDEA を使用しますが、任意の開発環境を使用できます。

IntelliJ IDEA を開き、File > New > Project を選択します。 開いたウィンドウで、以下に示すように、左下のAndroidを選択し、次に右側の空のアクティビティを選択します。

Android プロジェクト - 空のアクティビティ

Next ラベルのボタンを押して、表示されたウィンドウに CoroutineScope というプロジェクト名、com.coffeedev.coroutinescope というパッケージ名、言語セクションで Kotlin を選択し、最小SDKセクションで API 19 を選択してください。

これらの詳細が次のようになっていることを確認します。

プロジェクトの詳細

Create というラベルの付いたボタンを押して、新しい Android プロジェクトを生成します。 このアクションは、MainActivity という名前のアクティビティと activity_main という名前のレイアウトを含む新しいアプリケーションを作成します。

これらのファイルを使用して、このチュートリアルで取り上げる例をテストします。 必要な依存関係をアプリケーションに追加するには、アクティブなインターネット接続があることを確認してください。

コルーチンを操作するには、kotlinx-coroutines-core 依存関係をプロジェクトに追加する必要があります。 次の依存関係をコピーして build.gradle ファイルに貼り付け、コルーチンの依存関係を追加します。

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}

メイン レイアウトにボタンを作成する

src/main/res/layout の下にある acivity_main.xml レイアウト ファイルを開き、次のコードをコピーしてファイルに貼り付けます。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center_horizontal"
        tools:context=".MainActivity" >

    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:text="@string/button_text"/>

</LinearLayout>

このコードは、Button タイプの View を 1つだけ含む LinearLayout を作成します。 このボタンを使用して、アプリケーションでコルーチンを呼び出します。

最終的なレイアウトが次のようになっていることを確認します。

アプリのレイアウト

ボタンは、text 属性で示される文字列リソースを使用してラベル付けされます。 次の文字列リソースをコピーして、src/main/res/values フォルダーの下にある strings.xml という名前のファイルに貼り付けます。

これにより、ボタンのテキストが作成され、このテキストは button_text という名前を使用してアクセスされます。

<resources>
    <string name="app_name">CoroutineScope</string>
    <string name="button_text">Press Me</string>
</resources>

Kotlin で CoroutineScope を使用する

導入セクションで、新しいコルーチンを作成するには、スコープ内で作成する必要があると述べました。 これが CoroutineScope の出番です。

これを実際に表示するには、以下のコードをコピーして、src/main/java/com/coffeedev/coroutinescope フォルダーの下にある MainActivity.kt ファイルに貼り付けます。

package com.coffeedev.coroutinescope

import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button = findViewById<Button>(R.id.button)

        button.setOnClickListener {
            CoroutineScope(Dispatchers.Main).launch {
                val message = getLoopProduct();
                Toast.makeText(applicationContext, "Message: $message", Toast.LENGTH_LONG)
                    .show();
            }
        }

    }

    private suspend fun getLoopProduct(): Int {
        var value = 1;
        CoroutineScope(Dispatchers.IO).launch {
            for (number in 1..5) {
                delay(15);
                value *= number;
            }
        }
        return value;
    }

}

このコードでは、for ループの結果を返す getLoopProduct() という名前の suspend 関数を作成しました。 for ループは、CoroutineScope() の引数として渡される Dispatchers.IO スレッドを使用して実行されるコルーチンを使用して実行されます。

for ループの反復ごとに 15 ミリ秒の遅延が発生し、現在実行中のスレッドが中断されます。

onCreate() ライフ サイクル メソッドでは、単にメイン スレッドである Dispatchers.Main スレッドを使用して実行される新しいスコープを作成しました。 getLoopProduct() のコルーチンは、onCreate() メソッド内で作成されたコルーチンの子であることに注意してください。これは、suspend 関数が内部で呼び出されるためです。

異なるスコープから作成されたコルーチンは、独立して実行されます。 子コルーチンは親コルーチンとは異なるスコープを使用するため、親は子の実行が完了するまで待機しません。

このタイプの実行は、非構造化同時実行と呼ばれます。

onCreate() メソッドのコルーチンは 1 回だけ実行され、終了します。 これは、子コルーチンがバックグラウンドで引き続き実行され、アプリケーションがメモリ リークにさらされる可能性があることを意味します。

レイアウトで作成したボタンを使用して、getLoopProduct() によって返された値を含む Toast を表示します。 setOnClickListener() メソッドは、ボタンを押すと画面に Toast を表示します。

このコードを実行すると、子コルーチンの実行が完了する前に親コルーチンが中断されたため、Toast の値が 1 と表示されることに注意してください。

出力:

非構造化同時実行

Kotlin で coroutineScope を使用する

CoroutineScope()coroutineScope() の違いは、後者が新しいコルーチンを作成せずに新しいスコープを作成することです。 子コルーチンは親コルーチンスコープを使用します。これにより、親コルーチンが実行を完了する前に子コルーチンが完了することが保証されます。

このタイプの実行は、構造化同時実行と呼ばれます。

これを実際に見るには、前の例の suspend 関数を以下に示すものに置き換えます。

   private suspend fun getLoopProduct(): Int {
        var value = 1;
        coroutineScope {
            for (number in 1..5) {
                delay(15);
                value *= number;
            }
        }
        return value;
    }

onCreate() メソッドのコードは変更されないため、子コルーチンはメイン スレッドで実行される親スコープを使用して for ループを実行します。 親コルーチンは、子コルーチンが終了する前に for ループを実行するのを待ちます。

このコードを実行すると、Toast1 20 の値を表示することに注意してください。 2 は、親スコープを再利用するために、子コルーチンが終了せずにループ全体を実行していることを示します。

出力:

構造化された同時実行

まとめ

このチュートリアルでは、構造化されていない同時実行の範囲が子コルーチンにどのように影響するか、および構造化された同時実行を使用して問題を解決する方法を学びました。 これまで取り上げてきた主なトピックは、CoroutineScope() を使用して独立したスコープを作成する方法と、coroutineScope() を使用して親スコープを再利用する方法です。

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub

関連記事 - Kotlin Coroutine