在这篇文章中,我们将学习如何使用 OkHttp 拦截器。我们还将看到我们可以使用它的真实用例以及我们如何使用它来充分利用它。在 Android 中,我们有很多可以使用 OkHttp 拦截器完成的用例。
今天,我们将通过以下部分来掌握它:
- 什么是拦截器?
- 拦截器的类型。
- 如何在 OkHttpClient 中添加拦截器?
- 创建拦截器。
- 使用拦截器的真实用例。
什么是拦截器?
根据文档,拦截器是一种强大的机制,可以监视、重写和重试 API 调用。所以基本上,当我们做一些 API 调用时,我们可以监控调用或执行一些任务。
简单来说,Interceptor 就像机场安检过程中的安检人员。他们检查我们的登机牌,在上面盖章,然后让我们通过。
我们可以使用拦截器来做很多事情,例如,集中监控 API 调用。通常,我们需要为每个网络调用添加记录器,但是通过使用拦截器,我们可以集中添加一个记录器,这将适用于所有网络调用。另一个用例可以缓存网络调用的响应以构建离线优先应用程序,我们将在本博客后面详细了解它。
拦截器的类型
我们有两种类型的拦截器,如下所示:
- 应用程序拦截器:这些是添加在应用程序代码(我们编写的代码)和 OkHttp 核心库之间的拦截器。这些是我们使用addInterceptor() 添加的。
- 网络拦截器:这些是添加在 OkHttp 核心库和服务器之间的拦截器。这些可以使用addNetworkInterceptor() 添加到 OkHttpClient。
如何在 OkHttpClient 中添加拦截器?
在构建OkHttpClient对象时,我们可以添加如下拦截器:
fun myHttpClient(): OkHttpClient {
val builder = OkHttpClient().newBuilder()
.addInterceptor(/*our interceptor*/)
return builder.build()
}
在这里,在addInterceptor中,我们传递了我们创建的拦截器。现在,让我们看看如何创建拦截器。
创建拦截器
要创建拦截器,我们需要通过实现 Interceptor 接口来创建一个类,如下所示:
class MyInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
/**
* Our API Call will be intercepted here
*/
}
}
在这里,在intercept()中,我们可以在其中执行我们想要的任何操作。
要使用拦截器,我们可以像下面这样使用:
fun myHttpClient(): OkHttpClient {
val builder = OkHttpClient().newBuilder()
.addInterceptor(MyInterceptor())
return builder.build()
}
我们可以在addInterceptor() 中添加 MyInterceptor。
现在,让我们讨论更多可以使用拦截器的真实用例。
使用拦截器的真实用例
以下是 Android 中的常见用例:
集中记录错误
首先,我们需要创建ErrorInterceptor,如下所示:
class ErrorInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
val response = chain.proceed(request)
when (response.code()) {
400 -> {
//Show Bad Request Error Message
}
401 -> {
//Show UnauthorizedError Message
}
403 -> {
//Show Forbidden Message
}
404 -> {
//Show NotFound Message
}
// ... and so on
}
return response
}
}
- 首先,我们从chain.request()获取请求
- 然后,我们通过在chain.proceed(request)中传递请求来获取服务器返回的响应
- 现在,我们可以检查响应代码并执行操作。
- 我们可以通过接口或使用 RxJava、EventBus 等将错误传递给视图。
- 假设我们收到401 错误,即未经授权,那么我们可以执行一个操作来清除应用程序数据/注销用户或我们想要执行的任何操作。
现在,要在 OkHttpClient 中使用这个 ErrorInterceptor,我们可以像下面这样添加:
.addInterceptor(ErrorInterceptor())
这就是我们如何使用拦截器创建集中式错误记录器的方法。
OkHttp 有一个内置的记录器,对于调试非常有用。
注意:如果我们想记录 URL 重定向的详细信息,请考虑使用addNetworkInterceptor()在网络层使用拦截器。
缓存响应
如果我们想缓存 API 调用的响应,这样如果我们再次调用 API,响应就会从 Cache 中出来。
假设我们有从客户端到服务器的 API 调用,并且从服务器启用了Cache-Control标头 ,那么 OkHttp Core 将尊重该标头并缓存从服务器发送的响应一段时间。
但是如果没有从服务器启用 Cache-Control 怎么办。我们仍然可以使用拦截器缓存来自 OkHttp 客户端的响应。
只需看上图。在这里,我们要做的是,在进入 OkHttp Core 之前,我们必须拦截 Response 并添加 header(Cache-Control),所以它会被视为响应(带有 Cache-Control header)已经到来来自服务器,OkHttp Core 会尊重并缓存响应。
我们将创建一个拦截器,如下所示:
class CacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response: Response = chain.proceed(chain.request())
val cacheControl = CacheControl.Builder()
.maxAge(10, TimeUnit.DAYS)
.build()
return response.newBuilder()
.header("Cache-Control", cacheControl.toString())
.build()
}
}
在这里,我们有一个 CacheControl 用于为 Cache-Control 提供标头。
最后,我们可以添加如下:
.addNetworkInterceptor(CacheInterceptor())
在这里,如果我们看到,我们没有使用addInterceptor()而是使用addNetworkInterceptor()的用例。这是因为在这种情况下,操作发生在网络层。
但是,在构建离线优先应用程序时,我们需要考虑一些重要的事情。
只有当互联网可用时才会返回缓存的响应,因为 OkHttp 就是这样设计的。
- 当 Internet 可用并且数据被缓存时,它会从缓存中返回数据。
- 即使数据被缓存并且互联网不可用,它也会返回错误“没有互联网可用”。
现在要做什么?
除了上述之外,我们还可以在应用层使用以下ForceCacheInterceptor (CacheInterceptor,仅当未从服务器启用时)。要在代码中实现,我们将创建一个 ForceCacheInterceptor,如下所示:
class ForceCacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val builder: Request.Builder = chain.request().newBuilder()
if (!IsInternetAvailable()) {
builder.cacheControl(CacheControl.FORCE_CACHE);
}
return chain.proceed(builder.build());
}
}
我们可以在 OkHttpClient 中添加拦截器,如下所示:
.addNetworkInterceptor(CacheInterceptor()) // only if not enabled from the server
.addInterceptor(ForceCacheInterceptor())
在这里,我们使用 addInterceptor() 而不是 addNetworkInterceptor() 将 ForceCacheInterceptor 添加到 OkHttpClient,因为我们希望它在应用程序层上工作。
集中添加诸如访问令牌之类的标头
假设我们必须进行 API 调用,并且必须在所有 API 调用中添加授权标头。我们可以单独使用它,也可以使用拦截器集中它。
class AuthTokenInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val requestBuilder = originalRequest.newBuilder()
.header("Authorization", "AuthToken")
val request = requestBuilder.build()
return chain.proceed(request)
}
}
- 首先,我们从本地存储(如 SharedPreference)中获取标头的令牌。
- 在这里,我们使用chain.reques t() 拦截我们从应用程序触发的原始请求,并将其设置为originalRequest。
- 然后,我们通过添加带有进行网络调用所需的键和值的Header来再次构建请求。
- 然后,我们将再次构建请求并使用chain.proceed(request)通过传递具有 Authorization 标头的新请求来返回响应。
这就是我们可以集中所有 API 调用中常见的 Header 的方式。现在要在 OkHttpClient 中使用它,我们将执行以下操作:
.addInterceptor(AuthTokenInterceptor())
让我们转到另一个用例。
在单个位置刷新访问令牌
假设我们有一个用例,当我们在错误拦截器中收到401 错误时,我们需要刷新身份验证令牌,因为我们有一个未经授权的错误。我们可以使用以下方法做到这一点:
override fun intercept(chain: Interceptor.Chain): Response {
val accessToken = //our access Token
val request = chain.request().newBuilder()
.addHeader("Authorization", accessToken)
.build()
val response = chain.proceed(request)
if (response.code() == 401) {
val newToken: String = //fetch from some other source
if (newToken != null) {
val newRequest = chain.request().newBuilder()
.addHeader("Authorization", newToken)
.build()
return chain.proceed(newRequest)
}
}
return response
}
- 如果我们得到 response.code() 为401即未授权,我们将在此处刷新令牌,然后通过添加新标头来修改请求并向服务器发出新请求。
注意:在刷新 Access Token 时,另一种更灵活的方法是使用 OkHttp 的 Authenticator 接口。
现在,让我们转到另一个用例。
在 Android 端启用 Gzip
Gzip 用于数据压缩。在 Android 中,我们也可以使用 Gzip 来使用拦截器进行压缩。
因此,在得到响应时,OkHttp 会自动尊重头部(内容编码)并解压缩数据并返回,但是假设当我们必须将压缩数据发送到服务器时,我们必须编写自己的拦截器。
要使用拦截器,我们可以使用如下:
.addInterceptor(GzipRequestInterceptor())
所以,这些是真正的用例,我们如何在我们的 Android 应用程序中使用拦截器。我们可以用拦截器做很多事情。让我们开始充分利用它。
作者:Amit Shekhar
链接:OkHttp Interceptor - Making the most of it
网友评论