これは何ですか
ドキュメントをまだ読んでいない方-よく理解しておくことを強くお勧めします。
Jetbrainの記述:
コルーチンは非同期プログラミングを簡素化し、すべての複雑さをライブラリ内に残します。 プログラムロジックはコルーチンで順番に表現でき、ベースライブラリはそれを非同期に実装します。 ライブラリは、対応するイベントにサブスクライブするコールバックでユーザーコードの対応する部分をラップし、さまざまなスレッド(または異なるマシンにさえ)に実行をディスパッチできます。 コードは、厳密にシーケンシャルに実行される場合と同じくらい簡単です。
簡単に言えば、これは同期/非同期コード実行のためのライブラリです。
なんで?
RxJavaはもはや流行ではないからです(冗談です)。
第一に、私は何か新しいことを試してみたかったです。第二に、コルチンと他の方法の速度の比較という記事に出会いました。
例
たとえば、バックグラウンドで操作を実行する必要があります。
はじめに-コルチンへのbuild.gradleの依存関係を追加します。
| dependencies { |
| implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1" |
| implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1" |
| .... |
| } |
コードでメソッドを使用します。
| suspend fun <T> withContext( |
| context: CoroutineContext, |
| block: suspend CoroutineScope.() -> T |
| ) |
コンテキスト内-必要なスレッドプールを示します-単純な場合、これらはIO、メイン、デフォルトです
IO-APIを使用した簡単な操作、データベースを使用した操作、優先設定の共有など。
メイン-ビューにアクセスできるUIスレッド
デフォルト-CPU負荷が高い重い操作用
(この記事の詳細)
ブロック-実行したいラムダ
| var result = 1.0 |
| withContext(IO) { |
| for (i in 1..1000) { |
| result += i * i |
| } |
| } |
| Log.d("coroutines example", "result = $result") |
原則として、それだけです。1〜1000の平方和の結果を取得し、同時にメインスレッドをブロックしません。つまり、ANRはありません。
ただし、コルーチンが20秒間実行され、この間にデバイスを2回転させた場合、同時に実行されるブロックは3つになります。 おっと
そして、ブロックへのアクティビティへのリンクを渡した場合-リークと古いブロック内のビューで操作を実行する機能の欠如。 おっと。
それではどうしますか?
より良くする
| private var viewModelJob = Job() |
| private val viewModelScope = CoroutineScope(Dispatchers.Main + viewModelJob) |
| |
| fun doWork() { |
| var result = 1.0 |
| viewModelScope.launch { |
| withContext(IO) { |
| for (i in 1..1000) { |
| result += i * i |
| } |
| } |
| } |
| Log.d("coroutines example", " result = $result") |
| } |
| |
| fun cancelJob() { |
| viewModelJob.cancel() |
| } |
したがって、たとえば画面が回転したときなど、ストリーム内のコードの実行を停止することができました。
CoroutineScopeにより、ネストされたすべてのコルーチンのスコープを結合し、job.cancel()を呼び出すと実行が停止されました。
実行を停止した後にスコープを再利用する予定がある場合は、job.cancel()の代わりにjob.cancelChildren()を使用する必要があります。
同時に、フローを制御する機会がまだあります。
| fun doWork() { |
| var result = 1.0 |
| var result2 = 1.0 |
| viewModelScope.launch { |
| withContext(IO) { |
| for (i in 1..1000) { |
| result += i * i |
| } |
| } |
| withContext(Default) { |
| for (i in 1..1000) { |
| result2 += i * i |
| } |
| } |
| } |
| Log.d("coroutines example", "running result = $result, result 2 = $result2") |
| } |
| |
| fun cancelJob() { |
| viewModelJob.cancel() |
| } |
retrofit2を接続します
あられに依存関係を追加します。
| |
| dependencies { |
| implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" |
| implementation "com.squareup.retrofit2:converter-moshi:$converterMoshiVersion" |
| implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$retrofitCoroutinesVersion" |
| ... |
| } |
ペンhttps://my-json-server.typicode.com/typicode/demo/postsを例として使用します
レトロフィットインターフェイスについて説明します。
| interface RetrofitPosts { |
| |
| @GET("posts") |
| fun getPosts(): Deferred<Response<List<Post>>> |
| |
| } |
返されたPostモデルを説明します。
| data class Post(val id: Int, val title: String) |
BaseRepository:
| abstract class BaseRepository<Params, Result> { |
| |
| abstract suspend fun doWork(params: Params): Result |
| |
| } |
PostsRepositoryの実装:
| class PostsRepository : |
| BaseRepository<PostsRepository.Params, PostsRepository.Result>() { |
| |
| override suspend fun doWork(params: Params): Result { |
| val retrofitPosts = Retrofit |
| .Builder() |
| .baseUrl("https://jsonplaceholder.typicode.com") |
| .addConverterFactory(MoshiConverterFactory.create()) |
| .addCallAdapterFactory(CoroutineCallAdapterFactory()) |
| .build() |
| .create(RetrofitPosts::class.java) |
| val result = retrofitPosts |
| .getPosts() |
| .await() |
| |
| return Result(result.body()) |
| } |
| |
| class Params |
| data class Result(val posts: List<Post>?) |
| } |
BaseUseCase:
| abstract class BaseUseCase<Params, Result> { |
| |
| abstract suspend fun doWork(params: Params): Result |
| |
| } |
GetPostsListUseCaseの実装:
| class GetListOfPostsUseCase |
| : BaseUseCase<GetListOfPostsUseCase.Params, GetListOfPostsUseCase.Result>() { |
| |
| override suspend fun doWork(params: Params): Result { |
| return Result( |
| PostsRepository() |
| .doWork(PostsRepository.Params()) |
| .response |
| .posts |
| ) |
| } |
| |
| class Params |
| class Result(val posts: List<Post>?) |
| } |
結果は次のとおりです。
| fun doWork() { |
| val useCase = GetListOfPostsUseCase() |
| viewModelScope.launch { |
| withContext(Dispatchers.IO) { |
| |
| val result = useCase.doWork( |
| GetListOfPostsUseCase.Params() |
| ) |
| Log.d("coroutines example", "get list of posts = ${result.posts}") |
| } |
| } |
| |
| } |
| |
| fun cancelJob() { |
| viewModelJob.cancel() |
| } |
より良くする
私は怠け者で、コードシート全体をドラッグしたくないたびに、BaseViewModelで必要なメソッドを作成しました。
| abstract class BaseViewModel : ViewModel() { |
| |
| private var viewModelJob = Job() |
| private val viewModelScope = CoroutineScope(Dispatchers.Main + viewModelJob) |
| private var isActive = true |
| |
| // Do work in IO |
| fun <P> doWork(doOnAsyncBlock: suspend CoroutineScope.() -> P) { |
| doCoroutineWork(doOnAsyncBlock, viewModelScope, IO) |
| } |
| |
| // Do work in Main |
| // doWorkInMainThread {...} |
| fun <P> doWorkInMainThread(doOnAsyncBlock: suspend CoroutineScope.() -> P) { |
| doCoroutineWork(doOnAsyncBlock, viewModelScope, Main) |
| } |
| |
| // Do work in IO repeately |
| // doRepeatWork(1000) {...} |
| // then we need to stop it calling stopRepeatWork() |
| fun <P> doRepeatWork(delay: Long, doOnAsyncBlock: suspend CoroutineScope.() -> P) { |
| isActive = true |
| viewModelScope.launch { |
| while (this@BaseViewModel.isActive) { |
| withContext(IO) { |
| doOnAsyncBlock.invoke(this) |
| } |
| if (this@BaseViewModel.isActive) { |
| delay(delay) |
| } |
| } |
| } |
| } |
| |
| fun stopRepeatWork() { |
| isActive = false |
| } |
| |
| override fun onCleared() { |
| super.onCleared() |
| isActive = false |
| viewModelJob.cancel() |
| } |
| |
| private inline fun <P> doCoroutineWork( |
| crossinline doOnAsyncBlock: suspend CoroutineScope.() -> P, |
| coroutineScope: CoroutineScope, |
| context: CoroutineContext |
| ) { |
| coroutineScope.launch { |
| withContext(context) { |
| doOnAsyncBlock.invoke(this) |
| } |
| } |
| } |
| } |
投稿リストの取得は次のようになります。
| class PostViewModel : BaseViewModel() { |
| |
| val lengthOfPostsList = MutableLiveData<String>() |
| |
| fun getListOfPosts() { |
| doWork { |
| val result = GetListOfPostsUseCase() |
| .doWork(GetListOfPostsUseCase.Params()) |
| Log.d("coroutines example", "get list of posts = ${result.posts}") |
| lengthOfPostsList.postValue(result.posts?.size.toString()) |
| } |
| } |
おわりに
私はprodでコルーチンを使用しましたが、コードは実際にクリーンで読みやすいことが判明しました。
UPD:
レトロフィット例外処理の説明コメントを参照