【Android】Retrofit2.6で追加されたCoroutine対応について

# はじめに
Retrofitがバージョン2.6で`Kotlin Coroutines`の`suspend`関数に対応しました。

公式にCoroutineに対応した事によって、より簡単に非同期処理を実装できるようになりました。
ここでは`suspend`関数を使わない場合と使った場合の実装の違いを比較していきます。

# リポジトリ
[こちら](https://github.com/nanaten/RetrofitCoroutine)に全体のコードを置いておきます。

<details><summary>準備(折りたたんでいます)</summary>
<div>
今回は https://api.github.com/repos/octocat/Hello-World からidとnameを取得します。

## data class
まずGitHubのリポジトリを取得するためのクラスReposを作ります。
簡略化のためプロパティの数を絞っています。

```kotlin:Repos
data class Repos(
    val id: Int,
    val name: String
)
```

## Retrofit

Retrofitのインスタンス生成処理を記載します。
リポジトリでは`OkHttp3`も使っていますが本筋ではないので割愛します。

```kotlin:ApiService
class ApiService {

    private fun getRetrofit(): Retrofit {
        val moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()
        return Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .build()
    }

    fun get(): GithubApi {
        val retrofit = getRetrofit()
        return retrofit.create(GithubApi::class.java)
    }
}
```
**2020/01/07追記**
[JakeWharton](https://github.com/JakeWharton)氏曰く[「Gson is deprecated.」](https://www.reddit.com/r/androiddev/comments/ejjroe/kotlinx_json_vs_gson_a_short_article_about_kotlin/fcz3v0d/?utm_source=share&utm_medium=web2x)との事だったので、Gsonから[Moshi](https://github.com/square/moshi)に変更しました。

</details></div>
# suspend関数を使わない場合

`suspend`関数を使わない場合のAPIの実装は以下になります。

```kotlin:GithubApi
interface GithubApi {
    @GET("/repos/octocat/Hello-World")
    fun getRepos(): Call<Repos>
}
```


Reposをリクエスト結果を返す`Call`オブジェクトでラップしています。
リクエスト結果を`enqueue`を使ってコールバックで受け取ります。

```kotlin
    fun getRepos() {
        private val api = ApiService().get()
        api.getRepos().enqueue(object : Callback<Repos> {
            override fun onResponse(call: Call<Repos>, response: Response<Repos>) {
                // 成功時の処理
            }

            override fun onFailure(call: Call<Repos>, t: Throwable) {
                // 失敗時の処理
            }
        })
    }
```

# suspend関数を使う場合

```kotlin:GithubApi
interface GithubApi {
    @GET("/repos/octocat/Hello-World")
    suspend fun getReposWithCoroutine(): Repos
}
```

suspend関数を使う場合は関数の定義を` suspend fun `とします。
また、`Call`でラップせず、Reposをそのまま返すようにします。

取得処理は下記のようになります。

```kotlin
    fun getReposWithCoroutine() {
        private val api = ApiService().get()
        viewModelScope.launch {
            try {
                val repos = api.getReposWithCoroutine()
                // 成功時の処理
            }
            catch (e: Exception) {
                // 失敗時の処理
            }
        }
    }
```

コールバックを使わずにtry-catchでAPIリクエストを行えるため、コードがスッキリしました。


## TIPS：viewModelScopeについて
今回は取得処理をViewModelで行ったため、ここでは`viewModelScope`を使っています。

`viewModelScope`はViewModelとライフサイクルが一致しており、`onCleared()`で自動的にジョブをキャンセルしてくれるため、自分でCoroutineのライフサイクルを意識する必要がないという利点があります。

`viewModelScope`を使わず、Activityなどで処理する場合は

```kotlin
CoroutineScope(Dispatchers.Main).launch {
    ...
}
```

のようになるかと思います。

# おわりに
本記事を書くにあたり、以下のリンク先を参考にさせて頂きました。

[【アクトインディ開発者ブログ】Retrofit + Kotlin Coroutines](https://tech.actindi.net/2019/08/02/090000)
[【Qiita】Retrofit 2.6で通信を非同期処理](https://qiita.com/littleFeet/items/110a2a7237341feca804)