Kotlin协程与Retrofit 2.6.1完美合璧

协程有多么好用相比我们不需要再多赘述了,协程如何搭配旧版本的 Retrofit 使用相比大家也在网上看到过很多文章,大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//扩展 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.

1
2
@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 > ,以方便我们访问响应元数据。

实战:

1
2
3
4
5
6
try {
val response = ServiceCreator.create(PlaceService::class.java).login()
tv_detail.text = response
} catch (e: Throwable) {
e.printStackTrace()
}

整个网络请求看起来就像是在执行同步方法一样,更加简洁易读!