📜  Android中的OkHttp拦截器

📅  最后修改于: 2022-05-13 01:58:45.188000             🧑  作者: Mango

Android中的OkHttp拦截器

根据文档,拦截器是一种强大的机制,可以监视、重写和重试 API 调用。因此,当我们进行 API 调用时,我们可以对其进行监控或执行一些任务。简而言之,拦截器在安检过程中的函数类似于机场安检人员。他们检查了我们的登机牌,盖章,然后让我们通过。我们可以使用拦截器来做各种各样的事情,比如集中监控 API 调用。一般来说,我们需要为每个网络调用添加一个记录器,但是通过使用拦截器,我们可以添加一次记录器,它将适用于所有网络调用。另一个用例是缓存网络调用的响应以构建离线优先应用程序,我们将在本博客后面详细介绍。

拦截器类型

拦截器分为两类:

  1. 在应用程序代码(我们编写的代码)和 OkHttp 核心库之间添加的拦截器称为应用程序拦截器。这些是我们使用 addInterceptor() 添加的拦截器。
  2. 网络上的拦截器:这些是放置在 OkHttp 核心库和服务器之间的拦截器。这些可以通过使用 addNetworkInterceptor 命令()添加到 OkHttpClient。

你如何在 OkHttpClient 中包含拦截器?

我们可以在构建 OkHttpClient 对象的同时添加拦截器,如下图:

Kotlin
fun gfgHttpClient(): OkHttpClient {
     val builder = OkHttpClient().newBuilder()
         .addInterceptor(/*our interceptor*/)
     return builder.build()
 }


Kotlin
class gfgInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val aRequest: Request = chain.request()
        val aResponse = 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
    }
}


Kotlin
class gfgCache : Interceptor {
    override fun someInter(chain: Interceptor.Chain): Response {
        val aResponsive: Response = chain.proceed(chain.request())
        val someCache = CacheControl.Builder()
            .maxAge(12, TimeUnit.DAYS)
            .build()
        return response.newBuilder()
            .header("Geeks for Geeks Cache", cacheControl.toString())
            .build()
    }
}


Kotlin
class aGeeksforGeeksForce : Interceptor {
    override fun someIntercept(chain: Interceptor.Chain): Response {
        val aRandomBuilder: Request.Builder = chain.request().newBuilder()
        if (!IsInternetAvailable()) {
              // Check internet
            builder.someIntercept(CacheControl.FORCE_CACHE);
        }
        return chain.proceed(builder.build());
    }
}


Kotlin
class someGfgAuth : Interceptor {
    override fun aRandomCheck(chain: Interceptor.Chain): Response {
        val theOrignals = chain.request()
        val theBuilders = originalRequest.newBuilder()
            .header("GeeksforGeeks", "Auth")
        val spandan = requestBuilder.build()
        return chain.proceed(request)
    }
}


Kotlin
override fun gfgInter(chain: GfgInteror.Chain): Response {
    val gfgAuthToken = // our access Token
    val ourRequest = chain.ourRequest().newBuilder()
        .addHeader("Authorization", gfgAuthToken)
        .build()
    val response = chain.proceed(ourRequest)
    if (response.code() == 401) {
            val newToken: String =
            if (newToken != null) {
               val newOurRequest =  chain.ourRequest().newBuilder()
                    .addHeader("GFGAuth", newToken)
                    .build()
                return chain.proceed(newOurRequest)
            }
    }
    return response
}


Kotlin
.addAGFGInterceptor(GzipRequestInterceptor())


在 addInterceptor 中,我们可以包含 MyInterceptor()。现在,让我们看一些更真实的拦截器应用程序。

  1. 拦截器的实际应用
  2. 以下是 Android 中一些最常见的用例:
  3. 集中式错误记录

首先,我们必须构造如下所示的 ErrorInterceptor:

科特林

class gfgInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val aRequest: Request = chain.request()
        val aResponse = 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
    }
}
  1. 首先,我们从链中获取请求。要求()
  2. 然后通过链传递请求来获得来自服务器的响应。
  3. Proceed(request) 此时,我们可以检查响应代码并采取行动。
  4. 通过这个并等待结果

假设我们收到一个 401 错误,即未经授权,我们可以执行一个操作来清除应用程序数据/注销用户或我们想要的任何其他操作。

这就是我们如何使用拦截器来创建集中式错误记录器。

OkHttp 包含一个记录器,这对于调试非常有用。

将响应保存在缓存中

如果我们想缓存 API 调用响应,这样当我们再次调用 API 时,响应就会来自 Cache。如果我们有一个从客户端到服务器的 API 调用并且在服务器上启用了 Cache-Control 标头,OkHttp Core 将尊重该标头并将从服务器发送的响应缓存一段时间。但是如果服务器没有启用 Cache-Control 怎么办?使用拦截器,我们仍然可以缓存来自 OkHttp 客户端的响应。

看看上面的图片。我们这里需要做的是在进入 OkHttp Core 之前拦截 Response 并添加 header(Cache-Control),这样就被当作响应(带有 Cache-Control header)来自服务器,而 OkHttp Core将尊重这一点并缓存响应。

科特林

class gfgCache : Interceptor {
    override fun someInter(chain: Interceptor.Chain): Response {
        val aResponsive: Response = chain.proceed(chain.request())
        val someCache = CacheControl.Builder()
            .maxAge(12, TimeUnit.DAYS)
            .build()
        return response.newBuilder()
            .header("Geeks for Geeks Cache", cacheControl.toString())
            .build()
    }
}

如我们所见,在这种情况下我们没有使用 addInterceptor(),而是使用 addNetworkInterceptor()。这是因为在这种情况下,操作发生在网络层。但是在开发离线优先应用程序时,我们必须记住一件事。因为 OkHttp 就是这样设计的,所以只有在互联网可用时才会返回缓存的响应。当 Internet 可用并且数据被缓存时,它会从缓存中返回数据。即使数据被缓存并且互联网不可用,也会返回“没有互联网可用”的错误。

我现在该怎么办?

除了上面提到的之外,我们还可以在应用层使用下面列出的 ForceCacheInterceptor(CacheInterceptor,只有在服务器没有启用的情况下)。为了在代码中实现这一点,我们将创建一个 ForceCacheInterceptor,如下所示:

科特林

class aGeeksforGeeksForce : Interceptor {
    override fun someIntercept(chain: Interceptor.Chain): Response {
        val aRandomBuilder: Request.Builder = chain.request().newBuilder()
        if (!IsInternetAvailable()) {
              // Check internet
            builder.someIntercept(CacheControl.FORCE_CACHE);
        }
        return chain.proceed(builder.build());
    }
}

因为我们希望 ForceCacheInterceptor 在应用层工作,所以我们使用 addInterceptor() 而不是addNetworkInterceptor() 将其添加到 OkHttpClient。

在中心位置添加标头,例如访问令牌

假设我们需要进行 API 调用并在所有调用中包含 Authorization Header。我们可以单独使用它,也可以使用拦截器来集中它。

科特林

class someGfgAuth : Interceptor {
    override fun aRandomCheck(chain: Interceptor.Chain): Response {
        val theOrignals = chain.request()
        val theBuilders = originalRequest.newBuilder()
            .header("GeeksforGeeks", "Auth")
        val spandan = requestBuilder.build()
        return chain.proceed(request)
    }
}
  1. 首先,我们使用 SharedPreference 从本地存储中检索标头令牌。
  2. 在本节中,我们使用链拦截我们从应用程序触发的原始请求。
  3. request() 并将参数设置为 originalRequest。
  4. 然后通过包含带有进行网络调用所需的键和值的标头来重建请求。
  5. 然后,我们将重建请求并使用链返回响应。
  6. 通过传递带有授权标头的新请求来进行(请求)。

在单个位置刷新访问令牌

假设我们有一个用例,其中我们在错误拦截器中收到 401 错误,并且由于未经授权的错误需要刷新身份验证令牌。我们可以通过以下方式实现这一点:

科特林

override fun gfgInter(chain: GfgInteror.Chain): Response {
    val gfgAuthToken = // our access Token
    val ourRequest = chain.ourRequest().newBuilder()
        .addHeader("Authorization", gfgAuthToken)
        .build()
    val response = chain.proceed(ourRequest)
    if (response.code() == 401) {
            val newToken: String =
            if (newToken != null) {
               val newOurRequest =  chain.ourRequest().newBuilder()
                    .addHeader("GFGAuth", newToken)
                    .build()
                return chain.proceed(newOurRequest)
            }
    }
    return response
}

让我们继续讨论另一个用例。

在 Android 端启用 Gzip

Gzip 是一个数据压缩程序。使用拦截器,我们还可以在 Android 中使用 Gzip 进行压缩。所以,在收到响应时,OkHttp 会自动尊重头部(内容编码)并在返回前解压数据;但是,如果我们需要将压缩数据发送到服务器,我们必须编写自己的拦截器。

科特林

.addAGFGInterceptor(GzipRequestInterceptor())