協程有多麼好用相比我們不需要再多贅述了,協程如何搭配舊版本的 Retrofit 使用相比大家也在網上看到過很多文章,大致如下:
//擴展 await() 函數
private suspend fun <T> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
//await()的實質是調用 call的異步enqueue
enqueue(object : Callback<T> {
//請求失敗
override fun onFailure(call: Call<T>, t: Throwable) {
if (continuation.isCancelled) return // ② //如果協程已經取消了,無需繼續拋出異常
continuation.resumeWithException(t)
}
//請求成功
override fun onResponse(call: Call<T>, response: Response<T>) {
//1.3版本的新特性 使用 resumeWith(Result<T>)
continuation.resumeWith(runCatching { // ①
if (response.isSuccessful) {
response.body()
?: throw NullPointerException("Response body is null: $response")
} else {
throw HttpException(response)
}
})
}
})
//當協程取消時的回調函數,協程取消 - 請求取消
continuation.invokeOnCancellation {
try {
cancel()
} catch (ex: Throwable) { // ③
//Ignore cancel exception
//此時協程已經取消,請求取消是否存在異常已經沒有影響了
}
}
}
}
即,定義一個 Call 的擴展函數 await,掛起當前協程,執行 Call 的 enqueue 函數,在成功回調中 resume 協程,在失敗回調中拋出異常。
現在不需要這麼麻煩啦,只要使用最新版本的 Retrofit 2.6+ 版本,天然支持協程,網絡請求如同同步方法一樣易於書寫。
New: Support suspend modifier on functions for Kotlin! This allows you to express the asynchrony of HTTP requests in an idiomatic fashion for the language.
@GET("users/{id}")
suspend fun user(@Path("id") id: Long): User
Behind the scenes this behaves as if defined as fun user(…): Call and then invoked with Call.enqueue. You can also return Response for access to the response metadata.
現在你只需要在你的網絡請求方法前添加 suspend 修飾符,即可暢享協程帶來的便利。在幕後實現方式與上邊我們提到的方式大同小異,返回值可以是反序列化後的對象,也可以是 Response < T > ,以方便我們訪問響應元數據。
實戰:
try {
val response = ServiceCreator.create(PlaceService::class.java).login()
tv_detail.text = response
} catch (e: Throwable) {
e.printStackTrace()
}
整個網絡請求看起來就像是在執行同步方法一樣,更加簡潔易讀!