【Kotlin】そろそろRxを卒業して、Kotlin Coroutines Flow + RetrofitでAPIを叩いてみよう【Coroutine】

## 拝啓、RxJavaを使っている皆様へ
Coroutineはいいぞ
Coroutineこわくないよ

## 概要

現在Androidアプリでよく使われている技術といえば、RxJava(RxKotlin)です。
Retrofitを使ったAPI周りから、Listenerの代用など幅広く使えます。
一方で、明示的にDisposeを書いてあげないとメモリリークする危険などもあり、Rxちょっとな〜と思う場面も多いです。

さて、そんなRxに取って代わろうと言わんばかりに、Coroutine周りが進化し続けています。
今回の内容に関するところだけでいうと、

- [Coroutines Flow](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)の導入
- [Retrofit Version2.6からのCoroutine対応](https://github.com/square/retrofit/blob/master/CHANGELOG.md) (suspendつけられるようになった
- [便利なCoroutineScopeの標準実装](https://developer.android.com/topic/libraries/architecture/coroutines)

などがあります。
これらを駆使すれば、今までやってたRx + Retrofitを使ったAPIの実装なども、Coroutineで簡単にかけるようになりました。

今回はおなじみのGithub APIをCoroutine Flow + Retrofitを使ったパターンで叩いてみます。
Coroutineまだまだ難しくて導入しずらいな、という人の助けになれば幸いです。

## 前提
CoroutineとKotlin Coroutine Flowは既に多くの方が解説をしてくださっていますので、
まずはそちらを読んでいただくことをおすすめします。

- [Kotlin コルーチンを 理解しよう 2019](https://speakerdeck.com/sys1yagi/kotlin-korutinwo-li-jie-siyou-2019-kotlinfest2019)
- [雰囲気で利用しないためのAndroidにおけるKotlin-Coroutineメモ](https://qiita.com/offwhite/items/94540e7cc3b330507c9c)
- [Rx使いに送るKotlin Coroutine入門](https://qiita.com/Urotea/items/90fd1bc5e634e789d404)
- [5分でわかるKotlin Coroutines Flow](https://speakerdeck.com/sys1yagi/5fen-tewakarukotlin-coroutines-flow)

Flowとは、RxでいうところのObservableです。
Cold Streamで、collectを呼ぶことで動作開始します。

## Sample

[https://github.com/alpha2048/CoroutinesFlowTest](https://github.com/alpha2048/CoroutinesFlowTest)

## 解説

### Repository層 (API実装)

#### Rx

いままでRxを使ってGETを叩く場合は、以下のようなコードを書いていたケースが多いと思います。
Singleで返して、ViewModelなどでsubscribeするおなじみのパターンですね。

```
interface GithubApiInterface {
    @GET("/search/repositories")
    fun getGithubRepository(@Query("q") q: String,
                            @Query("page") page: Int): Single<RepoResponse>
}

class GithubRepository {
    fun getRepositoryList(q: String, page: Int): Single<RepoResponse> {
        val retrofit = Retrofit.Builder().略
        val service = retrofit.create(GithubApiInterface::class.java)
        return service
            .getGithubRepository(q, page)
    }
}
```

#### Coroutine

これをCoroutine対応します。
Retrofitは2.6 からsuspendがつけられるようになったので、suspendに直します。
そして、Responseを取得しつつ、Repository側でFlowを作って流すようにします。

```
interface GithubApiInterface {
    @GET("/search/repositories")
    suspend fun getGithubRepository(@Query("q") q: String,
                            @Query("page") page: Int): RepoResponse
}

class GithubRepository {
    suspend fun getRepositoryList(q: String, page: Int): Flow<RepoResponse> = flow {
        val retrofit = Retrofit.Builder().略
        val service = retrofit.create(GithubApiInterface::class.java)
        emit(service.getGithubRepository(q, page))
    }.flowOn(Dispatchers.IO)
}
```

### ViewModel層

#### Rx

こちらもいつものパターンですね
受け取った結果をSubscribeしてなにかします。また、Subjectなどのトリガーを発火させたりします。
ちゃんとDisposeしてあげないといけないですが、うっかり忘れてしまうことも少なくないです。

```
GithubRepository().getRepositoryList("Coroutine", 1)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread()) 
    .subscribe { t1, t2 -> 
        repositoryList.addAll(t1)
        trigger.accept(Unit)
    } 
    .addTo(compositeDisposable)
```

#### Coroutine

APIの取得をviewModelScopeで起動させることで、ViewModelのライフサイクルで動くようにします。
Disposeを書く必要がないので手間も少なくいい感じです。
また、イベントとしてBroadcastChannelを用意して、取得時に通知を出してあげます。
(BroadcastChannelは1対n用なので、今回の使い方では1対1用のChannelでもよい)

```
viewModelScope.launch(Dispatchers.Main) { 
    GithubRepository().getRepositoryList("Coroutine", 1).collect{
        repoItems.addAll(it.items) 
        trigger.send(Unit)
    }
}
```

### Activity側

lifecycleScopeでイベントを受け取って、RecyclerViewの更新処理を行います。
こちらもDisposeを書く手間が減っています。


## おわりに
Coroutineがここまで進化していたとなると、そろそろRxからの本格卒業を検討すべきかもしれないですね。
いずれはCoroutineを使った実装がデフォルトになっていくのかも？

## 2020/5/4 追記
Koinを使ったDIも追加しました
DIに興味がありましたらそちらも見てみてください〜〜
[Koin](https://github.com/InsertKoinIO/koin)


## 参考資料

わたしもコルーチンへの理解はまだまだなところがあるので、色々な資料を読みました。
コードはこちらで動かしてみつつ、下記の資料を参考に理解を深めてくださいませ。

### 解説
- [Kotlin コルーチンを 理解しよう 2019](https://speakerdeck.com/sys1yagi/kotlin-korutinwo-li-jie-siyou-2019-kotlinfest2019)
- [雰囲気で利用しないためのAndroidにおけるKotlin-Coroutineメモ](https://qiita.com/offwhite/items/94540e7cc3b330507c9c)
- [Rx使いに送るKotlin Coroutine入門](https://qiita.com/Urotea/items/90fd1bc5e634e789d404)
- [5分でわかるKotlin Coroutines Flow](https://speakerdeck.com/sys1yagi/5fen-tewakarukotlin-coroutines-flow)
- [DroidKaigi 2020 アプリでの学び【Kotlin Coroutines Flow 編】](https://qiita.com/mkeeda/items/1d39b422fd6fb9dc3554)

### 実装
- [ViewModelとKotlin Coroutinesの書き方あれこれ](https://satoshun.github.io/2019/12/android-viewodel-kotlin-coroutine/)
- [Kotlin Flow on Android — Quick guide](https://proandroiddev.com/kotlin-flow-on-android-quick-guide-76667e872166)
- [今からAndroidにCoroutineを取り入れたときのアーキテクチャについて](https://dev.classmethod.jp/articles/android-droidkaigi-sample/)