Kotlinで待機処理をマスター!実用的なコード15選

Kotlinプログラムのコード例と、待機処理のシンボルKotlin
この記事は約20分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Kotlinは現代のアプリケーション開発において、非常に人気のあるプログラミング言語です。

特にAndroidアプリ開発においては、Googleが公式にサポートしている言語として多くの開発者に愛用されています。

この記事を読めば、Kotlinでの待機処理を適切に実装することができるようになります。

待機処理は非同期処理の一部として、特定の条件が満たされるのを待つために使用されます。

しかし、正しく実装しないとアプリケーションのパフォーマンスに影響を与える可能性があります。

そこで、この記事では待機処理の基本から高度な応用までを詳細に解説します。

●Kotlinの待機処理とは

待機処理とは、特定の条件が満たされるまでプログラムの実行を一時的に停止する処理のことを指します。

例えば、ユーザーからの入力を待つ、外部APIのレスポンスを待つ、データベースの処理結果を待つなど、さまざまなシチュエーションで必要とされます。

○待機処理の基本

Kotlinでの基本的な待機処理は、Threadクラスのsleepメソッドを使用して実現することができます。

sleepメソッドは、指定したミリ秒数だけ実行を一時停止します。

このコードでは、Thread.sleepを使って1000ミリ秒、つまり1秒間の待機を行っています。

fun main() {
    println("待機開始...")
    Thread.sleep(1000)
    println("1秒後の出力")
}

このコードを実行すると、”待機開始…”の出力後、約1秒間待機した後に”1秒後の出力”と表示されます。

○待機処理の利点と用途

待機処理には次のような利点と用途があります。

  1. プログラムの実行を一時的に中断して、外部リソースの準備やユーザーのアクションを待つことができます。
  2. 複数のタスクを同時に行う際に、特定のタスクの完了を待つために使用できます。
  3. 外部APIやデータベースとの通信時に、応答を待つために使用することが多いです。

待機処理は、以上のような様々な場面で使用される非常に便利な処理です。

しかし、適切に使用しないとアプリケーションのパフォーマンスやレスポンスに悪影響を及ぼす可能性があるため、注意が必要です。

●Kotlinでの待機処理の使い方

Kotlinでの待機処理の実装は、非常にシンプルで直感的に行うことができます。

しかし、実際にコーディングを行う前に基本的な仕組みを理解することで、効率的かつ正確に待機処理を実装することが可能になります。

○サンプルコード1:基本的な待機処理の実装

Kotlinの標準ライブラリには、待機処理を行うためのメソッドや関数が豊富に用意されています。

その中でも最も基本的な方法として、Thread.sleepメソッドを使用する方法があります。

下記のコードでは、Thread.sleepを用いて、2秒間の待機を行っています。

fun main() {
    println("2秒待機します...")
    Thread.sleep(2000)
    println("待機が終了しました。")
}

このコードを実行すると、「2秒待機します…」と出力した後、約2秒の間何も処理が行われず、その後に「待機が終了しました。」と表示されます。

○サンプルコード2:特定の条件での待機処理

単に時間を指定して待機するだけではなく、特定の条件が満たされるまで待機するといった処理もよく行われます。

例えば、あるリストの中に特定の要素が含まれるまで待機する、といった条件での待機が考えられます。

下記のコードは、リストnumbers5が追加されるまで待機し、5が追加されたら待機を終了してメッセージを表示するものです。

fun main() {
    val numbers = mutableListOf(1, 2, 3, 4)

    // 別のスレッドで5を追加
    Thread {
        Thread.sleep(3000)
        numbers.add(5)
    }.start()

    while (5 !in numbers) {
        Thread.sleep(500)  // 500ミリ秒ごとに条件チェック
    }

    println("リストに5が追加されました!")
}

このコードを実行すると、3秒後に「リストに5が追加されました!」と出力されることが確認できます。

○サンプルコード3:非同期処理との組み合わせ

Kotlinでは、coroutinesを利用して非同期処理を効率よく実装できます。

待機処理と非同期処理を組み合わせることで、ユーザーエクスペリエンスの向上やリソースの効率的な利用が期待できます。

下記のサンプルコードは、非同期的にデータの取得を行い、その結果を待機して表示するシンプルな例です。

import kotlinx.coroutines.*

fun main() = runBlocking {
    println("データの取得を開始します...")

    val data: Deferred<String> = async {
        delay(3000) // 3秒間のシミュレーション
        "取得したデータ"
    }

    println("他の処理を実行...")

    val result = data.await()  // データの取得が完了するまで待機
    println("データの取得が完了しました: $result")
}

このコードでは、asyncを用いて非同期にデータの取得をシミュレートしています。

取得処理には3秒の遅延が設けられており、この間にメインスレッドは他の処理を実行しています。

awaitメソッドを利用して非同期タスクの結果を待機し、完了したら結果を表示しています。

このコードを実行すると、最初に「データの取得を開始します…」と表示され、続けて「他の処理を実行…」と表示されます。

その後、約3秒後に「データの取得が完了しました: 取得したデータ」と表示されることが確認できます。

○サンプルコード4:複数の待機処理の組み合わせ

複数の待機処理を同時に扱う場面も考えられます。Kotlinのcoroutinesでは、asynclaunchを使用して、複数の待機処理を並行して実行することが可能です。

下記のコードは、2つの非同期処理を同時に開始し、両方の結果を待機する例を表しています。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val firstTask: Deferred<String> = async {
        delay(2000)
        "タスク1の結果"
    }

    val secondTask: Deferred<String> = async {
        delay(5000)
        "タスク2の結果"
    }

    val firstResult = firstTask.await()
    val secondResult = secondTask.await()

    println("結果1: $firstResult, 結果2: $secondResult")
}

このコードでは、2つの非同期タスクを定義しており、それぞれ異なる遅延時間を持ちます。

awaitメソッドで両タスクの完了を待機し、その結果を表示しています。

このコードを実行すると、最初のタスクが2秒後、次のタスクが5秒後に完了します。

両タスクの結果が得られた後、「結果1: タスク1の結果, 結果2: タスク2の結果」と表示されます。

○サンプルコード5:待機時間のカスタマイズ

待機処理において、待機する時間をカスタマイズすることは非常に重要です。

Kotlinでの待機時間のカスタマイズは、delay関数を使用することで、指定した時間だけ待機することができます。

この関数は、ミリ秒単位で時間を指定します。

ここでは、delay関数を使った待機時間のカスタマイズ例を紹介します。

import kotlinx.coroutines.*

fun main() = runBlocking {
    println("カスタマイズした待機時間のスタート")

    val customWaitTime = 4000L // 4秒
    delay(customWaitTime)

    println("カスタマイズした待機時間の終了")
}

このコードでは、customWaitTimeという変数に4秒をミリ秒単位で設定し、その時間だけdelay関数を使用して待機しています。

このように、必要に応じて待機する時間を柔軟に設定することができます。

コードを実行すると、「カスタマイズした待機時間のスタート」と表示された後、4秒待機した後に「カスタマイズした待機時間の終了」と表示されます。

この待機処理の時間をカスタマイズする方法は、例えばAPIのレスポンス待ち時間の設定や、アニメーションの遅延時間の調整など、さまざまなシーンで役立ちます。

●待機処理の応用例

Kotlinでの待機処理は多岐にわたるシーンで役立てることができます。

ここでは、いくつかの具体的な待機処理の応用例と、それぞれのシーンでの実装方法を解説します。

○サンプルコード6:APIの応答を待つ場合

Web APIとの通信時、サーバーからの応答を待つ必要があります。

このような場面で待機処理を適切に実装することで、ユーザーエクスペリエンスを向上させることができます。

import kotlinx.coroutines.*
import java.net.HttpURLConnection
import java.net.URL

fun main() = runBlocking {
    val apiEndpoint = "https://api.example.com/data"

    val responseData: String = async {
        val connection = URL(apiEndpoint).openConnection() as HttpURLConnection
        connection.inputStream.bufferedReader().use { it.readText() }
    }.await()

    println("APIから取得したデータ: $responseData")
}

このコードでは、非同期処理を行いながらAPIの応答を待っています。

asyncを使用して非同期にAPIを呼び出し、その結果をawaitで待ち受けています。

上記コードを実行すると、APIから取得したデータが表示されます。

このように、外部APIとの通信時には、適切な待機処理を組み込むことで、データ取得をスムーズに行うことが可能です。

○サンプルコード7:ユーザー入力を待つ場合

ユーザーからの入力を待つ場面でも、待機処理が役立ちます。

例えば、ユーザーがボタンをクリックするまで処理を停止したい場合などに使用できます。

import kotlinx.coroutines.*
import java.awt.event.ActionEvent
import javax.swing.JButton
import javax.swing.JFrame
import kotlinx.coroutines.channels.Channel

fun main() = runBlocking {
    val channel = Channel<Unit>()

    val frame = JFrame("ユーザー入力待ち")
    val button = JButton("クリックしてください").apply {
        addActionListener { e: ActionEvent? ->
            channel.offer(Unit)
        }
    }

    frame.add(button)
    frame.setSize(300, 200)
    frame.isVisible = true

    channel.receive()

    println("ボタンがクリックされました")
    frame.dispose()
}

上記コードでは、Swingを利用してシンプルなウィンドウを表示し、ユーザーがボタンをクリックするのを待っています。

ボタンがクリックされた際に、channelを通してシグナルを送信し、そのシグナルをreceiveで待機します。

ボタンをクリックすると「ボタンがクリックされました」というメッセージが表示されます。

この方法を利用することで、ユーザーの操作に応じて待機処理を実装することが可能です。

○サンプルコード8:データベースの読み書きを待つ場合

データベース操作は、プログラミングにおいて頻繁に行われるタスクの一つです。

特に、大量のデータを扱う場合や複数のクエリを実行する場面では、適切な待機処理が不可欠となります。

ここでは、Kotlinでのデータベース操作時における待機処理の実装例をご紹介します。

import kotlinx.coroutines.*
import java.sql.Connection
import java.sql.DriverManager
import java.sql.ResultSet

fun main() = runBlocking {
    val jdbcUrl = "jdbc:mysql://localhost:3306/mydb"
    val dbUser = "user"
    val dbPass = "password"

    val query = "SELECT * FROM users WHERE age > 20"

    val data: ResultSet = async(Dispatchers.IO) {
        val connection: Connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)
        val statement = connection.createStatement()
        statement.executeQuery(query)
    }.await()

    while(data.next()) {
        println("User ID: ${data.getInt("id")}, Name: ${data.getString("name")}")
    }
}

このコードでは、MySQLデータベースにアクセスし、20歳以上のユーザーを取得しています。

asyncawaitを使用して非同期にデータベースクエリを実行し、その結果を待機しています。

コードの特徴として、Dispatchers.IOを指定してI/Oバウンドのタスクを効率的に実行しています。

これにより、データベースとの通信中に他のタスクがブロックされることなく、高速に処理を進めることができます。

実際に上記のコードを実行すると、指定した条件に一致するユーザーのIDと名前がコンソールに表示されます。

○サンプルコード9:他のアクティビティやサービスとの連携

Androidのアプリケーション開発では、異なるアクティビティやサービス間でのデータのやり取りが頻繁に行われます。

こうした場面で待機処理を適切に実装することで、アプリケーションの動作をスムーズにすることができます。

import kotlinx.coroutines.*
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch {
            val data = fetchDataFromService()
            // データを使用した処理
        }
    }

    private suspend fun fetchDataFromService(): String {
        delay(2000)  // サービスからデータを取得するのをシミュレート
        return "Service Data"
    }
}

このコードでは、lifecycleScopeを使用してアクティビティのライフサイクルに連動したコルーチンを起動しています。

fetchDataFromService関数は、サービスからデータを取得する処理をシミュレートしています。

delay(2000)を使用して、2秒の待機を模倣していますが、実際のアプリケーションでは、外部のサービスからデータを非同期に取得する処理を実装することが想定されます。

上記のコードを実行すると、2秒後にサービスから取得したデータを使用した処理が開始されます。

このように、Kotlinのコルーチンを使用して、アクティビティやサービス間での非同期処理を効果的に実装することができます。

○サンプルコード10:エラーハンドリングを伴う待機処理

待機処理中にエラーが発生することは珍しくありません。

例えば、ネットワーク通信中に接続が途切れる、APIからのレスポンスが予期しない形式である、データベースへのアクセス中にエラーが発生するなど、様々なシチュエーションが考えられます。

Kotlinのコルーチンは、エラーハンドリングを簡単かつ効率的に行うことができます。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        try {
            processData()
        } catch (e: Exception) {
            println("エラーが発生しました:${e.message}")
        }
    }
    job.join()
}

suspend fun processData() {
    delay(1000)  // 1秒の待機を模倣
    throw Exception("データ処理中にエラー")
}

このコードでは、processData関数内で1秒の待機後に例外をスローしています。

メイン関数内のlaunchブロックでこの関数を呼び出しており、例外が発生した場合はcatchブロックでエラーメッセージを出力しています。

このコードを実行すると、1秒の待機後に「エラーが発生しました:データ処理中にエラー」というメッセージがコンソールに表示されます。

このように、Kotlinのコルーチンを使用すると、非同期処理中のエラーハンドリングもシンプルに記述することができます。

●注意点と対処法

Kotlinの待機処理は、非同期処理の魅力的な機能の一つですが、適切に使用しないと様々な問題が生じる可能性があります。

ここでは、待機処理に関連した典型的な問題点と、それに対する対処法を紹介します。

○サンプルコード11:無限ループになる可能性のある待機処理

待機処理の中で条件が満たされるまで繰り返し処理を行う場合、条件が永遠に満たされない状況が発生すると、プログラムは無限ループに陥る恐れがあります。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    while (System.currentTimeMillis() - startTime < 5000) { // 5秒経過するまでループ
        delay(100)
    }
    println("5秒経過しました")
}

このコードを実行すると、5秒後に「5秒経過しました」と表示されます。

しかし、ループの条件を誤って設定すると、無限ループが発生してしまう可能性が高まります。

無限ループを避けるためには、ループの回数や時間に上限を設けるなどの工夫が必要です。

○サンプルコード12:メモリリークを防ぐ方法

待機処理や非同期処理の中でオブジェクトを生成して利用すると、そのオブジェクトが不要になった後もメモリ上に残ってしまうことがあります。

これはメモリリークと呼ばれ、アプリケーションのパフォーマンス低下の原因となります。

import kotlinx.coroutines.*

var bigData: ByteArray? = null

fun main() = runBlocking {
    launch {
        bigData = ByteArray(100_000_000) //大量のデータを確保
        delay(5000)
        bigData = null
    }
    delay(6000)
    println("完了")
}

このコードでは、大量のデータを確保して5秒後にnullを代入していますが、実際にメモリが解放されるかはJVMの動作に依存します。

メモリリークを防ぐためには、使用後のオブジェクトを適切に解放することが重要です。

○サンプルコード13:複数スレッドでの待機処理の管理

多くのアプリケーションでは、複数のスレッドで待機処理を行う場面があります。

このとき、各スレッドでの待機処理の状態を適切に管理することが求められます。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val jobs = List(10) {
        launch {
            delay((it + 1) * 100L)
            println("スレッド$it 完了")
        }
    }
    jobs.forEach { it.join() }
    println("すべてのスレッドが完了しました")
}

このコードでは、10個のスレッドでそれぞれ異なる待機時間を持つ待機処理を行い、すべてのスレッドが完了したことを確認してからメッセージを表示しています。

●カスタマイズ方法

Kotlinの待機処理はそのままでも多くの場面で役立ちますが、時として特定の要件に合わせてカスタマイズする必要が出てきます。

ここでは、Kotlinの待機処理をカスタマイズする方法と、その実用的な例を解説します。

○サンプルコード14:カスタム待機処理のクラスの作成

待機処理を再利用する場合や、特定の機能を持った待機処理を実装したい場合、カスタムクラスを作成することが考えられます。

import kotlinx.coroutines.delay

class CustomWaiter(private val waitTime: Long) {
    suspend fun waitForIt() {
        println("カスタム待機を開始します")
        delay(waitTime)
        println("カスタム待機が終了しました")
    }
}

fun main() {
    val customWaiter = CustomWaiter(3000)
    customWaiter.waitForIt()
}

このコードでは、指定した待機時間に基づいてカスタムの待機処理を行うCustomWaiterクラスを作成しています。

このクラスを使用すると、3秒待機した後に終了メッセージが表示されます。

○サンプルコード15:外部ライブラリを利用した高度な待機処理

外部のライブラリやフレームワークを利用することで、待機処理のカスタマイズや拡張を更に進めることができます。

例として、特定の条件下での待機や、複数の待機処理の同時実行などの高度な待機処理を実現することが考えられます。

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull

fun main() = runBlocking {
    val result = withTimeoutOrNull(3000) {
        repeat(5) { i ->
            println("待機中: $i")
            delay(1000)
        }
        "完了"
    }
    println(result ?: "タイムアウトしました")
}

上記のコードでは、withTimeoutOrNull関数を使用して、指定した時間内に処理が完了しない場合にタイムアウトさせています。

このコードを実行すると、3秒後に「タイムアウトしました」というメッセージが表示されます。

まとめ

Kotlinでの待機処理は非常に強力であり、様々な状況や要件に応じて使いこなすことができます。

基本的な待機処理からカスタム待機、さらには外部ライブラリを用いた高度な待機処理まで、幅広い技術や方法が存在します。

本記事を通して、待機処理の基本的な使い方やその応用、さらにはカスタマイズ方法といった様々な側面を解説してきました。

Kotlinの待機処理の機能や利点を最大限に活かすことで、効率的で可読性の高いコードを実現することができます。

Kotlinでのコーディングを進める中で、待機処理の知識や技術を活かすことで、より高度なアプリケーションやシステムの実装が可能となります。

今後も新しい技術や方法が登場することでしょう。

常に最新の情報や知識を追い求め、自らの技術を磨き続けることで、プログラミングでの成功をつかみ取ることができるでしょう。