はじめに
Retrofitがバージョン2.6でKotlin Coroutines
のsuspend
関数に対応しました。
公式にCoroutineに対応した事によって、より簡単に非同期処理を実装できるようになりました。
ここではsuspend
関数を使わない場合と使った場合の実装の違いを比較していきます。
リポジトリ
こちらに全体のコードを置いておきます。
まずGitHubのリポジトリを取得するためのクラスReposを作ります。 Retrofitのインスタンス生成処理を記載します。 2020/01/07追記準備(折りたたんでいます)
今回は https://api.github.com/repos/octocat/Hello-World からidとnameを取得します。
data class
簡略化のためプロパティの数を絞っています。data class Repos(
val id: Int,
val name: String
)
Retrofit
リポジトリではOkHttp3
も使っていますが本筋ではないので割愛します。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)
}
}
JakeWharton氏曰く「Gson is deprecated.」との事だったので、GsonからMoshiに変更しました。
suspend関数を使わない場合
suspend
関数を使わない場合のAPIの実装は以下になります。
interface GithubApi {
@GET("/repos/octocat/Hello-World")
fun getRepos(): Call<Repos>
}
Reposをリクエスト結果を返すCall
オブジェクトでラップしています。
リクエスト結果をenqueue
を使ってコールバックで受け取ります。
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関数を使う場合
interface GithubApi {
@GET("/repos/octocat/Hello-World")
suspend fun getReposWithCoroutine(): Repos
}
suspend関数を使う場合は関数の定義をsuspend fun
とします。
また、Call
でラップせず、Reposをそのまま返すようにします。
取得処理は下記のようになります。
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などで処理する場合は
CoroutineScope(Dispatchers.Main).launch {
...
}
のようになるかと思います。
おわりに
本記事を書くにあたり、以下のリンク先を参考にさせて頂きました。
【アクトインディ開発者ブログ】Retrofit + Kotlin Coroutines
【Qiita】Retrofit 2.6で通信を非同期処理