Kotlin coroutines on Android
A coroutine is concurrency design pattern that you can use onAndroid to simplify code that executes asynchronously.Coroutines was added to Kotlin in version 1.3 and are based on establishedconcepts from other languages.
On Android, coroutines help to manage long running tasks that mightotherwise block main thread and cause your app to be unresponsive.このトピックでは、Kotlin のコルーチンを使用してこれらの問題に対処し、よりクリーンで簡潔なアプリ コードを記述できるようにする方法について説明します。
- 軽量。 コルーチンが実行されているスレッドをブロックしないサスペンドをサポートするため、1つのスレッドで多くのコルーチンを実行することができます。 サスペンドは、多くの同時処理をサポートしながら、ブロッキングよりもメモリを節約します。
- ビルトイン キャンセルのサポート: キャンセルは、実行中のコルーチン階層を通して自動的に伝搬されます。 多くの Jetpack ライブラリは、コルーチンの完全なサポートを提供する拡張機能を含んでいます。
Examples overview
Guide to the app architecture に基づき、このトピックの例はネットワーク要求を行い、結果をメインスレッドに返し、そこでアプリはユーザーに結果を表示することができます。 このガイドでは、コルーチンを使用してメイン スレッドをブロックされないようにするさまざまな解決策を繰り返します。
ViewModel
には、コルーチンで直接動作する KTX 拡張のセットがあります。 これらの拡張機能はlifecycle-viewmodel-ktx
ライブラリであり、このガイドで使用されています。
Dependency info
あなたの Android プロジェクトでコルーチンを使用するには、次の依存関係をアプリの build.gradle
ファイルに追加します:
dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'}
Executing in a background thread
Main thread でネットワーク要求を作成すると、応答を受信するまで待つ、つまりブロックするようになります。 スレッドがブロックされているため、OS は onDraw()
を呼び出すことができず、アプリがフリーズし、潜在的に ANR (Application Not Responding) ダイアログにつながる可能性があります。
First, let’s take at our Repository
class and see how it’smaking the network request:
sealed class Result<out R> { data class Success<out T>(val data: T) : Result<T>() data class Error(val exception: Exception) : Result<Nothing>()}class LoginRepository(private val responseParser: LoginResponseParser) { private const val loginUrl = "https://example.com/login" // Function that makes the network request, blocking the current thread fun makeLoginRequest( jsonBody: String ): Result<LoginResponse> { val url = URL(loginUrl) (url.openConnection() as? HttpURLConnection)?.run { requestMethod = "POST" setRequestProperty("Content-Type", "application/json; utf-8") setRequestProperty("Accept", "application/json") doOutput = true outputStream.write(jsonBody.toByteArray()) return Result.Success(responseParser.parse(inputStream)) } return Result.Error(Exception("Cannot open HttpURLConnection")) }}
makeLoginRequest
is synchronous and blocks the calling thread.より良い使用経験のために、この操作を背景スレッド上で実行します。
ViewModel
は、ユーザーがたとえばボタンをクリックすると、ネットワーク リクエストをトリガーします:
class LoginViewModel( private val loginRepository: LoginRepository): ViewModel() { fun login(username: String, token: String) { val jsonBody = "{ username: \"$username\", token: \"$token\"}" loginRepository.makeLoginRequest(jsonBody) }}
前のコードでは、LoginViewModel
はネットワーク リクエストを作成するときに UI スレッドをブロックしています。 メイン スレッドから実行を移す最も簡単な方法は、新しいコルーチンを作成し、I/O スレッドでネットワーク リクエストを実行することです。 すべてのコルーチンはアスコープで実行されなければならないことに注意してください。
launch
は、コルーチンを生成し、その関数本体の実行を対応するディスパッチャにディスパッチする関数である。 login
関数は次のように実行されます:
- アプリはメインスレッドの
View
レイヤーからlogin
関数を呼び出します。 -
launch
は新しいコルーチンを作成し、ネットワーク要求は I/O 処理用に確保したスレッドで独立に実行されます。
このコルーチンはviewModelScope
で開始されているので、ViewModel
のスコープ内で実行される。 ユーザーが画面から離れるために ViewModel
が破棄されると、viewModelScope
は自動的にキャンセルされ、実行中のすべてのコルーチンがキャンセルされます。
前の例での 1 つの問題は、makeLoginRequest
を呼び出すものは明示的にメインスレッドから実行を移動することを忘れない必要があることです。
Use coroutines for main-safety
We consider a function main-safe when it doesn’t block UI updates on themain thread.これは、メイン スレッドでの UI 更新をブロックしない関数をメイン安全と見なします。 メインスレッドから makeLoginRequest
を呼び出すと UI がブロックされるため、makeLoginRequest
関数はメインセーフではありません。 Coroutines ライブラリの withContext()
関数を使用して、コルーチンの実行を別のスレッドに移動します。
class LoginRepository(...) { ... suspend fun makeLoginRequest( jsonBody: String ): Result<LoginResponse> { // Move the execution of the coroutine to the I/O dispatcher return withContext(Dispatchers.IO) { // Blocking network request code } }}
withContext(Dispatchers.IO)
はコルーチンの実行を I/O スレッドに移動し、呼び出し関数をメインセーフにするとともに必要に応じて UI の更新をできるようにします。 このキーワードは、コルーチン内から呼び出される関数を強制するための Kotlin の方法である。makeLoginRequest
はメインスレッドから実行を移動するので、login
関数のコルーチンはメインスレッドで実行できるようになります。
このコードは前の login
の例といくつかの点で異なっています:
-
launch
はDispatchers.IO
パラメータをとりません。launch
にDispatcher
を渡さない場合、viewModelScope
から起動されたすべてのコルーチンはメインスレッドで実行されます。 - ネットワーク要求の結果は、後継の障害 UI を表示するように処理されるようになりました。
ログイン関数は次のように実行されます:
- アプリはメインスレッド上の
View
層からlogin()
関数を呼び出します。 -
launch
はメインスレッド上でネットワーク要求を行う新しいコールーチンを作り、コールーチンが実行を開始しました。 - コルーチン内で
loginRepository.makeLoginRequest()
now を呼び出すと、makeLoginRequest()
のwithContext
ブロックが終了するまでコルーチンの実行が停止します。 withContext
ブロックが終了すると、login()
のコルーチンがネットワーク要求の結果を使用してメインスレッド上で実行を再開します。
例外の処理
Repository
層が投げることができる例外を処理するには、Kotlin に組み込まれている例外サポートを使用します。次の例では、try-catch
ブロックを使用しています。
Leave a Reply