响应式 协程
In the past, we used to use JakeWharton/retrofit2-kotlin-coroutines-adapter in order to use Retrofit with Coroutines.
过去,我们曾经使用JakeWharton / retrofit2-kotlin-coroutines-adapter来将Retrofit与Coroutines一起使用。
But since Retrofit supports the suspend
modifier on functions for Kotlin now, we don’t have to do this anymore.See Retrofit version 2.6.0 (2019–06–05)
但是由于Retrofit现在支持Kotlin函数上的suspend
修饰符,因此我们不再需要这样做。请参见Retrofit版本2.6.0(2019–06–05)
So now using Retrofit with Coroutines is as simple as this
因此,现在将Coroutines与Retrofit一起使用就这么简单
@GET("users/{id}")
suspend fun user(@Path("id") id: Long): User
Enough talking about using Retrofit and Coroutines because there are tons of articles about this and let’s talk about error handling.
足够讨论使用Retrofit和Coroutines的问题,因为有很多关于此的文章,让我们谈谈错误处理。
In this article I will show how can we create our own Retrofit CallAdapter to handle the API calls errors and success states.
在本文中,我将展示如何创建自己的Retrofit CallAdapter来处理API调用错误和成功状态。
By the end of the article you should be able to do the following:
在本文末尾,您应该可以执行以下操作:
创建网络响应密封类 (Create Network Response Sealed Class)
First, let’s model our responses states by creating a sealed class that represents the API call response states.
首先,让我们通过创建一个表示API调用响应状态的密封类来对响应状态进行建模。
Most probably we need 4 states:
我们很可能需要4个状态:
Success
which is a data class that should contain the body of the success state of the request.Success
是数据类,应包含请求成功状态的主体。ApiError
which represents the non-2xx responses, it also contains the error body and the response status code.ApiError
代表非2xx响应,它还包含错误正文和响应状态代码。NetworkError
which represents network failure such as no internet connection cases.NetworkError
表示网络故障,例如没有Internet连接情况。UnknownError
which represents unexpected exceptions occurred creating the request or processing the response, for example parsing issues.代表发生意外异常的
UnknownError
发生在创建请求或处理响应(例如解析问题)时。
创建我们自己的呼叫转换器 (Create our own Call transformer)
In order to make Retrofit return NetworkResponse
when topic()
API call is triggered, we need to write a custom CallAdapter
为了在触发topic()
API调用时使Retrofit返回NetworkResponse
,我们需要编写一个自定义的CallAdapter
First step to create our own CallAdapter
is implementing Call
interface from Retrofit
创建我们自己的CallAdapter
是从Retrofit实现Call
接口
The method that has most of the logic in our implementation of the Call
interface is enqueue
.
enqueue
是我们实现Call
接口时具有大多数逻辑的方法。
What is the enqueue
method?
入enqueue
方法是什么?
Asynchronously send the request and notify callback of its response or if an error occurred talking to the server, creating the request, or processing the response.
异步发送请求,并将响应通知给回调,或者如果在与服务器交谈,创建请求或处理响应时发生错误,则将其通知。
So we will implement enqueue
method and check the response, then return the correct callback.
因此,我们将实现enqueue
方法并检查响应,然后返回正确的回调。
enqueue
takes a callback which has two methods to implement:
enqueue
采用回调,该回调具有两种实现方法:
onResponse
: which is invoked for a received HTTP response, this response could be success response or failure one.onResponse
:为收到的HTTP响应调用,此响应可以是成功响应,也可以是失败响应。So we have to check here if the response is successful, we return the success state of our
因此,我们必须在此处检查响应是否成功,然后返回我们的成功状态
NetworkResponse
sealed classNetworkResponse
密封类If it’s not a success response, we try to parse the error body as the expected error data class we provide as a type, if the parse succeeded we return the error as
如果不是成功响应,则尝试将错误正文解析为我们作为类型提供的预期错误数据类,如果解析成功,则将错误返回为
ApiError
state, otherwise it’sUnknownError
.ApiError
状态,否则为UnknownError
。onFailure
: which is invoked when a network exception occurred talking to the server or when an unexpected exception occurred creating the request or processing the response.onFailure
:当发生网络异常与服务器通信或发生意外异常以创建请求或处理响应时调用。Here we can simply check if the exception is
在这里,我们可以简单地检查异常是否为
IOException
then we return theNetworkError
state, otherwise it should beUnknownError
state.IOException
那么我们返回NetworkError
状态,否则就应该是UnknownError
状态。
enqueue method
enqueue method
The rest of the methods of Call
interface are simple, we will just delegate them to the original call.
Call
接口的其余方法很简单,我们只将它们委托给原始调用。
So the our Call
implementation should look like this
因此,我们的Call
实现应如下所示
创建自己的CallAdapter (Create your own CallAdapter)
Now it’s time to create our CallAdapter
现在是时候创建我们的CallAdapter
Creating CallAdapter
is very straight forward, we will need to implement only two methods
创建CallAdapter
非常简单,我们只需要实现两种方法
responseType
Returns the value type that this adapter uses when converting the HTTP response body to a Java object
responseType
返回此适配器将HTTP响应主体转换为Java对象时使用的值类型。
adapt
Returns an instance of T which delegates to call, here we will use our NetworkResponseCall
that we just created.
adapt
返回T的一个实例,委托该实例进行调用,这里我们将使用刚刚创建的NetworkResponseCall
。
创建自己的CallAdapter Factory (Create your own CallAdapter Factory)
Next step is creating our CallAdapter.Factory
下一步是创建我们的CallAdapter.Factory
CallAdapter.Factory
has only one abstract method that we should implement which is get
.
CallAdapter.Factory
只有我们应该实现的一种抽象方法get
。
get
method in the CallAdapter.Factory
should return a callback adapter for interface methods that it could handle or null if it’s can’t be handled by this factory.
CallAdapter.Factory
中的get
方法应该为它可以处理的接口方法返回一个回调适配器,如果该工厂无法处理,则返回null。
So simply our get
method in our custom CallAdapter.Factory
should check if the returnType
is our sealed class for the API response calls, and then handle it.If the caller isn’t asking for our Sealed Class, return null, this isn’t the right adapter.
所以简单地在自定义CallAdapter.Factory
get
方法应该检查returnType
是否是API响应调用的密封类,然后对其进行处理。如果调用方不要求我们的密封类,则返回null,这不是正确的适配器。
What does this mean?
这是什么意思?
But there is something we need to know first about how suspend
functions work with Retrofit.
但是,我们首先需要了解有关suspend
功能如何与Retrofit一起工作的一些知识。
@GET("users/{id}")
suspend fun user(@Path("id") id: Long): User
Behind the scenes this behaves as if defined as fun user(...): Call<User>
and then invoked with Call.enqueue
在幕后,它的行为就像定义为fun user(...): Call<User>
,然后用Call.enqueue
调用
So this means when we have a suspend function like this
所以这意味着当我们有这样的暂停功能时
suspend fun user(): ApiResponse<User, Error>
It’s actually
其实是
fun user(): Call<ApiResponse<User, Error>>
So after removing the Call
type we have to make sure that the inner type is ApiResponse
因此,在删除Call
类型之后,我们必须确保内部类型为ApiResponse
Now we reached a point, where we have the response as ApiResponse<Success, Error>
现在,我们到达了一个响应点,即ApiResponse<Success, Error>
Next, we need extract the success and error types from the ApiResponse
parameterized type.
接下来,我们需要从ApiResponse
参数化类型中提取成功和错误类型。
The base CallAdapter.Factory
in Retrofit has a function named getParameterUpperBound
which should help us getting the success/error types from the parameterized type ApiResponse
CallAdapter.Factory
中的基本CallAdapter.Factory
有一个名为getParameterUpperBound
的函数,该函数应该帮助我们从参数化类型ApiResponse
获取成功/错误类型。
Then get the error converter and return
然后获取错误转换器并返回
使改造知道您的呼叫适配器 (Make retrofit aware of your Call Adapter)
Finally you need to add our custom CallAdapterFactory to Retrofit while initializing it
最后,您需要在初始化时将我们的自定义CallAdapterFactory添加到Retrofit
专家提示: (Protip:)
If you happen to have a generic error model that your API uses for most/all of the endpoints you can save yourself sometimes and code by creating a typealias
that wraps this generic error model and the success type like this
如果您碰巧有一个API用于大多数/所有端点的通用错误模型,则有时可以保存自己并编写代码,方法是创建一个包装该通用错误模型和成功类型的类型typealias
typealias GenericResponse<S> = NetworkResponse<S, GenericApiError>
响应式 协程