モデルを返すAPI( Retrofit によって実装)が与えられます。拡張関数を使用して、昔ながらの Call をDeferred
にラップします。
_fun <T> Call<T>.toDeferred(): Deferred<T> {
val deferred = CompletableDeferred<T>()
// cancel request as well
deferred.invokeOnCompletion {
if (deferred.isCancelled) {
cancel()
}
}
enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>?, t: Throwable) {
deferred.completeExceptionally(t)
}
override fun onResponse(call: Call<T>?, response: Response<T>) {
if (response.isSuccessful) {
deferred.complete(response.body()!!)
} else {
deferred.completeExceptionally(HttpException(response))
}
}
})
return deferred
}
_
これで、次のようなモデルを取得できます。
_data class Dummy(val name: String, val age: Int)
fun getDummy(): Deferred<Dummy> = api.getDummy().toDeferred()
_
しかし、どうすればDeferred
内のオブジェクトをmodifyして、Deferred
を返すことができますか。
_fun getDummyAge(): Deferred<Int> {
// return getDummy().age
}
_
私はコルーチンに慣れていないので、これはここで物事が行われる方法ではないかもしれません。私が RxJava ファンだとすると、次のようにこのケースを実装します。
_fun getDummy(): Single<Dummy> = api.getDummy().toSingle()
fun getDummyAge(): Single<Int> = getDummy().map { it.age }
_
では、Deferred
関数からgetDummyAge
を返そうとすべきでしょうか?または、可能な場合はいつでも_suspended fun
_を宣言し、すべてのAPIのメソッドでdeferred.await()
を呼び出す方がよいでしょうか?
async styleのプログラミングに従う場合、つまり_Deferred<T>
_を返す関数を作成する場合は、async functiongetDummyAge
を次のように定義できます。
_fun getDummyAge(): Deferred<Int> = async { getDummy().await().age }
_
ただし、このスタイルのプログラミングは、Kotlinでは一般的に推奨されていません。慣用的なKotlinアプローチは、次の署名を使用して拡張関数の一時停止Call<T>.await()
を定義することです。
_suspend fun <T> Call<T>.await(): T = ... // more on it later
_
そして、それを使用して次のように記述しますサスペンド関数getDummy
タイプの結果を返すDummy
直接遅延にラップせずに:
_suspend fun getDummy(): Dummy = api.getDummy().await()
_
この場合、簡単に書くことができますサスペンド関数getDummyAge
:
_suspend fun getDummyAge(): Int = getDummy().age
_
Retrofit呼び出しの場合、次のようにawait
拡張機能を実装できます。
_suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine { cont ->
cont.invokeOnCompletion { cancel() }
enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>?, t: Throwable) {
cont.resumeWithException(t)
}
override fun onResponse(call: Call<T>?, response: Response<T>) {
if (response.isSuccessful) {
cont.resume(response.body()!!)
} else {
cont.resumeWithException(HttpException(response))
}
}
})
}
_
非同期関数とサスペンド関数のスタイルの違いについて詳しく知りたい場合は、KotlinConf 2017の コルーチンの概要 をご覧になることをお勧めします。次に、 設計ドキュメントのこのセクション もいくつかの洞察を提供します。