SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

【最新Kotlinアップデート解説】バージョン1.1からの変更点総まとめ

Kotlin最新アップデートまとめ ──非同期処理を実現する機能コルーチンを紹介

【最新Kotlinアップデート解説】バージョン1.1からの変更点総まとめ 第5回

 本連載は、Kotlinのバージョン1.1から2.0までのアップデート内容を、テーマごとにバージョン横断で紹介する連載です。本連載も、今回でいったんの区切りとなります。前回は、クラスやインターフェースに関する変更点を紹介する後編として、委譲プロパティとインラインクラスに関する変更点を紹介しました。今回は、非同期処理を実現する仕組みであるコルーチンを紹介します。

コルーチンの基本

 本連載は、Kotlinのバージョン1.1から2.0までのアップデート内容を、テーマごとにバージョン横断で紹介する連載です。その連載も、今回でいったんの区切りとなります。今回のテーマは、コルーチンです。

非同期処理とコルーチン

 コルーチンは、Kotlinで非同期処理を実現するための機能です。Android開発においては、多用される仕組みですが、実は、Kotlin言語のバージョン1.0の正式リリース時には存在せず(より正確には実験的機能であり)、後発のバージョン1.3で正式導入されたものです。

 図1は、main()関数から、longProcess()関数とshow()関数を呼び出している処理の流れを図式化したものです。そして、それぞれの関数を呼び出している間、main()関数の処理は待ち状態となります。他の関数から、実行処理がmain()に戻ると同時に、main()関数内の続きの処理が再開します。

 つまり、関数の戻りと処理の再開が同期することから、この仕組みを同期処理と言い、Kotlinの処理体系はその基本が同期処理となります。

図1:同期処理は関数の処理戻りと再開の同期
図1:同期処理は関数の処理戻りと再開の同期

 ここで、longProcess()関数内の処理に時間がかかるものとします。すると、main()関数内の処理は続きの処理ができなくなります。つまり、処理がブロックされます。

 これを避けるために、図2のように、main()関数はlongProcess()関数の処理の戻りを待たずに続きの処理を行うようにします。その場合、関数からの処理の戻りと処理の再開が同期しません。

 この仕組みを、非同期処理と言います。あるいは、main()関数内の処理がブロックされないことから、ノンブロッキング処理とも言います。

図2:関数の処理戻りをまたずに関数を呼び出す非同期処理
図2:関数の処理戻りをまたずに関数を呼び出す非同期処理

 Kotlin言語の元となる(JVM言語の主と言える)Java言語では、この非同期処理を実現するためにスレッドの分離という方法を採用せざるを得ません。もちろん、同じJVM上で動作するKotlinでは、同じくスレッドの分離という方法も可能ですが、同一スレッドで動作した上で非同期処理を実現できる仕組みとして、コルーチンが導入されています。

コルーチンとは処理を中断できる仕組み

 コルーチンの仕組みを、先の図1で表すと図3の通りです。

 図3の緑の部分のように、longProcess()の呼び出しから戻りまでの一連の処理をコルーチンとして定義します。すると、Kotlinのランタイム(実行環境)は、もしコルーチン内の処理に時間がかかるようならば、いつでもその処理を中断し、コルーチン外の続きの処理を先に行えるようになっています。その後、コルーチン内の処理の完了を待ちます。これにより、非同期処理が実現できるようになっています。

図3:処理を中断して続きの処理を先に行えるのがコルーチン
図3:処理を中断して続きの処理を先に行えるのがコルーチン

コルーチンの定義はlaunchメソッド

 コルーチンを定義する構文は、以下の通りです。CoroutineScopeインスタンスのlaunch()メソッドを実行し、その引数ラムダ式内にコルーチンとして定義したい処理、つまり、非同期で行いたい処理を記述します。

[構文]コルーチンの定義
コルーチンスコープ.launch {
  コルーチンとして定義したい処理
}

 ということは、まず、コルーチンスコープ(CoroutineScopeインスタンス)を用意する必要があります。これが、Android開発ならば、SDK内に用意されており、アクティビティやフラグメントなどのライフサイクル対応コンポーネント内で利用できるlifecycleScopeと、ViewModel内で利用できるviewModelScopeがあります。

 例えば、lifecycleScopeで図3の処理を実現したコード例を挙げると、リスト1のようになります。

[リスト1]lifecycleScopeを使ったコルーチンのコード例
override fun onCreate(savedInstanceState: Bundle?) {
    :
  lifecycleScope.launch {
    longProcess()
  }
  show()
}

ピュアKotlinでのコルーチン定義

 一方、ピュアなKotlinでコルーチンを定義する場合は、表1の2種類の方法があります。

表1:ピュアKotlinでのコルーチンの定義方法
方法 利用できる場所
runBlocking関数内 main()関数内
coroutineScope関数内 suspend関数内

 このうち、まずrunBlocking()関数の使い方を紹介します。

 例えば、リスト2のコードの通りです。なお、リスト2中で利用しているprintMsgWithTime()関数は、引数のメッセージをその時点での時刻とスレッド番号とともに標準出力する関数です。

[リスト2]runBlockingのコード例
fun main() {
  runBlocking {  // (1)
    printMsgWithTime("main開始")  // (2)
    launch {  // (3)
      printMsgWithTime("コルーチン開始")  // (4)
      val startTime = LocalTime.now()
      while(true) {
        val now = LocalTime.now()
        val duration = Duration.between(startTime, now)
        val diff = duration.toMillis()
        if(diff > 1000) {
          break
        }
      }
      printMsgWithTime("コルーチン終了")  // (5)
    }
    printMsgWithTime("main終了")  // (6)
  }
}

 リスト2の(3)でlaunch()メソッドを実行しています。すると、その引数ラムダ式内の処理、すなわち、launch以降の{ }ブロック内の処理がコルーチンとなります。

 ただし、そのlaunch()メソッドの前にはCoroutineScopeインスタンスを表す変数が記述されていません。代わりに記述されているのが(1)のrunBlocking()関数の実行です。main()関数内でrunBlocking()関数を実行すると、その引数ラムダ式、すなわち、(2)~(6)までのコード内では暗黙的インスタンスとしてCoroutineScopeが存在することになります。もしこれを変数で表すとするなら、thisです。つまり(3)のコードは、正確に記述するならば、this.launchとなります。ただし、このthisは通常記述しませんので、単にlaunchと記述します。

 これが、表1のひとつめのlaunch()メソッドの実行方法です。

コルーチンが非同期処理になることの確認

 ここで、リスト2の実行結果を確認しながら、処理内容を見ていきましょう。

 リスト2の実行結果にはその時点での時刻とスレッド番号が含まれているので、その都度違います。例えば、リスト3の通りになります。各行の末尾の3という数字がスレッド番号です。なお、実行結果中のコメントの番号は、リスト2のコードに対応しています。

[リスト3]リスト2の実行結果例
main開始: 02:50:51.528402; 3  // (2)
main終了: 02:50:51.532604; 3  // (6)
コルーチン開始: 02:50:51.535779; 3  // (4)
コルーチン終了: 02:50:52.536883; 3  // (5)

 リスト2のコルーチン内では、無限ループを実行し、1秒(1000ミリ秒)経過するとループを抜けるようになっています。このことから、リスト2の(4)と(5)の間には1秒間の経過が発生します。実際に、実行結果(リスト3)の(4)と(5)を見ると、その通りになっています。

 ここで注目すべきなのは、リスト2の(6)の実行順序です。1秒というコンピュータからすると長い時間かかる処理というのをコルーチンにすることによって、その処理を後回しにし、先にコルーチン外の(6)の処理を行っているのが実行結果からわかります。

 これにより、図3の通り、コルーチンを利用することで非同期処理が実現できていることがわかります。

次のページ
コルーチンを含めることができるsuspend関数

この記事は参考になりましたか?

【最新Kotlinアップデート解説】バージョン1.1からの変更点総まとめ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/22420 2025/10/29 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング