Android网络框架——OKHttp拦截器

(三)OkHttp网络请求拦截器详解 这个拦截器一般做网络调试用,用户自定义,插在连接拦截器和请求服务拦截器之间。整个OkHttp的网络请求工作都是在这个五大拦截器中完成。他们分别负责不同的工作,如果流水线一样,经过五到工序,最终完成了完整的工作。 阅读详情
AI摘要

本文深入解析OkHttp中的Interceptor机制,涵盖其作用、类型及使用方式。重点介绍Application与Network两种拦截器的区别,并结合源码分析BridgeInterceptor的工作原理,帮助开发者实现请求响应的动态修改、日志记录、认证等通用逻辑。

目录

一、Interceptor 的作用

二、Interceptor 的类型

1. Application Interceptor(应用拦截器)

2. Network Interceptor(网络拦截器)

3.源码解读

三、Interceptor使用


在 OkHttp 中,Interceptor 是一个非常强大的功能,用于拦截、修改、观察或终止 HTTP 请求和响应的处理流程。它主要在 网络请求的生命周期 中插入自定义逻辑,从而实现灵活的操作。

一、Interceptor 的作用

  1. 拦截和修改请求

    • 在请求发送到服务器之前,对其进行修改。例如,添加公共的请求头、参数、认证信息或日志记录。
    • 修改请求的 URL、方法(GET、POST 等)或请求体。
  2. 拦截和修改响应

    • 在服务器返回响应之后、传递给调用者之前,处理响应数据。例如,解密数据、缓存处理、或统一错误处理。
  3. 全局控制网络请求:

    • 实现统一的网络请求日志、认证、重试策略等逻辑。
  4. 支持链式调用:

    • Interceptor 的设计模式基于责任链(Chain of Responsibility),可以在拦截器链中依次处理请求和响应,决定是否继续传递。

二、Interceptor 的类型

OkHttp 中有两种类型的拦截器:

1. Application Interceptor(应用拦截器)

  • 在应用层级运行。

  • 主要用于请求的整体修改或全局操作,例如添加公共头部信息、统一日志打印等。

  • 特点:

    • 不会处理重定向。
    • 不会触发失败重试。
    • 在缓存处理之前执行。
  • 添加方法:

    OkHttpClient.Builder()
        .addInterceptor { chain ->
            val request = chain.request().newBuilder()
                .addHeader("Authorization", "Bearer token")
                .build()
            chain.proceed(request)
        }
        .build()
    
    

2. Network Interceptor(网络拦截器)

  • 运行在网络层级。

  • 主要用于低级网络操作,例如查看和修改原始网络数据包、监控网络性能。

  • 特点:

    • 可以观察原始数据包(如头部和体)以及网络请求的实际传输。
    • 能够查看响应从服务器返回到客户端的完整过程。
    • 支持失败重试。
  • 添加方法:

    OkHttpClient.Builder()
        .addNetworkInterceptor { chain ->
            val request = chain.request()
            val response = chain.proceed(request)
            // 打印响应时间等
            response
        }
        .build()
    
    

3.源码解读

通过源码看出,Interceptor是一个抽象接口,有五个实现类,看名称分别对应桥接、缓存、请求拦截,链接拦截、重试拦截。

此拦截包含请求发送前的相关拦截,以及服务端响应体返回的拦截处理,如果我们对资源以及网络请求做处理,那么就需要使用拦截器,并且处理该事件。

以BridgeInterceptor为例子,源码如下,其他的Interceptor也是类似

class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
    val userRequest = chain.request() // 获取原始请求
    val requestBuilder = userRequest.newBuilder() // 创建请求构建器

    val body = userRequest.body // 获取请求体
    if (body != null) {
        val contentType = body.contentType() // 获取内容类型
        if (contentType != null) {
            requestBuilder.header("Content-Type", contentType.toString()) // 设置Content-Type请求头
        }

        val contentLength = body.contentLength() // 获取内容长度
        if (contentLength != -1L) {
            requestBuilder.header("Content-Length", contentLength.toString()) // 设置Content-Length请求头
            requestBuilder.removeHeader("Transfer-Encoding") // 移除Transfer-Encoding请求头
        } else {
            requestBuilder.header("Transfer-Encoding", "chunked") // 设置Transfer-Encoding请求头为chunked
            requestBuilder.removeHeader("Content-Length") // 移除Content-Length请求头
        }
    }

    if (userRequest.header("Host") == null) {
        requestBuilder.header("Host", userRequest.url.toHostHeader()) // 设置Host请求头
    }

    if (userRequest.header("Connection") == null) {
        requestBuilder.header("Connection", "Keep-Alive") // 设置Connection请求头为Keep-Alive
    }

    // 如果添加了"Accept-Encoding: gzip"请求头,那么我们需要解压传输流
    var transparentGzip = false
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
        transparentGzip = true
        requestBuilder.header("Accept-Encoding", "gzip") // 设置Accept-Encoding请求头为gzip
    }

    val cookies = cookieJar.loadForRequest(userRequest.url) // 加载请求的cookie
    if (cookies.isNotEmpty()) {
        requestBuilder.header("Cookie", cookieHeader(cookies)) // 设置Cookie请求头
    }

    if (userRequest.header("User-Agent") == null) {
        requestBuilder.header("User-Agent", userAgent) // 设置User-Agent请求头
    }

    val networkResponse = chain.proceed(requestBuilder.build()) // 继续执行链中的下一个拦截器

    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers) // 接收响应头

    val responseBuilder = networkResponse.newBuilder() // 创建响应构建器
        .request(userRequest) // 设置请求

    // 如果响应使用了gzip压缩,并且请求头中指定了接受gzip压缩,并且响应体存在
    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
        val responseBody = networkResponse.body // 获取响应体
        if (responseBody != null) {
            val gzipSource = GzipSource(responseBody.source()) // 创建gzip源
            val strippedHeaders = networkResponse.headers.newBuilder()
                .removeAll("Content-Encoding") // 移除Content-Encoding响应头
                .removeAll("Content-Length") // 移除Content-Length响应头
                .build()
            responseBuilder.headers(strippedHeaders) // 设置修改后的响应头
            val contentType = networkResponse.header("Content-Type") // 获取Content-Type响应头
            responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer())) // 设置解压后的响应体
        }
    }

    return responseBuilder.build() // 构建并返回响应
    }

  /** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */
  private fun cookieHeader(cookies: List<Cookie>): String = buildString {
    cookies.forEachIndexed { index, cookie ->
      if (index > 0) append("; ")
      append(cookie.name).append('=').append(cookie.value)
    }
  }
}

这段代码的主要逻辑如下:

  • 它首先获取原始请求,并创建一个请求构建器。

  • 如果请求有请求体,它会设置Content-Type和Content-Length请求头,或者设置Transfer-Encoding为chunked并移除Content-Length。

  • 如果请求中没有Host请求头,它会添加一个Host请求头。

  • 如果请求中没有Connection请求头,它会添加一个Connection请求头为Keep-Alive。

  • 如果请求中没有指定接受gzip压缩,并且没有Range请求头,它会添加一个Accept-Encoding请求头为gzip。

  • 从cookieJar中加载请求的cookie,并添加到请求头中。

  • 如果请求中没有User-Agent请求头,它会添加一个User-Agent请求头。

  • 继续执行链中的下一个拦截器,获取网络响应。

  • 接收网络响应的头部信息。

  • 如果网络响应使用了gzip压缩,它会创建一个解压后的响应体,并移除Content-Encoding和Content-Length响应头。

  • 最后构建并返回响应。

这个拦截器用于确保请求和响应的完整性和正确性,包括设置必要的请求头,处理内容编码和压缩,以及处理cookie。

三、Interceptor使用

  • 创建自定义的 Interceptor 类:
class CustomInterceptor : Interceptor {
    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        // 对请求进行操作,例如添加头部信息
        val newRequest = request.newBuilder()
            .header("New-Header", "New-Value")
            .build()

        val response = chain.proceed(newRequest)
        // 对响应进行操作,例如处理错误响应
        if (response.code() == 401) {
            // 处理401未授权错误
            // 可以在这里重试请求或者返回一个自定义的响应
        }
        return response
    }
}
  • 将 Interceptor 添加到 OkHttpClient 中:
val client = OkHttpClient.Builder()
    .addInterceptor(CustomInterceptor())
    .build()

val request = Request.Builder()
    .url("https://example.com")
    .build()

val call = client.newCall(request)

call.enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
        // 处理响应
    }

    override fun onFailure(call: Call, e: IOException) {
        // 处理请求失败
    }
})

参考文章

https://juejin.cn/post/7451043837906190362

# Okhttp解析—Interceptor详解 Okhttp解析—Interceptor详解 Interceptor可以说是okhttp的精髓之一,Okhttp重写请求/响应、重试、缓存响应等操作,基本都是在各个Interceptor中完成的,上篇文章分析Okhttp运行流程时只是简单带过,那么这篇文章就来详细分析下Interceptor以及拦截器链机制。 一、Interceptor以及InterceptorChain /** * Ob... 阅读详情

相关推荐

Android OKHttp 可能你从来没用过的拦截器 【实用推荐】

在你的全局OkHttp中添加 Interceptor new OkHttpClient.Builder() .addInterceptor(new CaptureInfoInterceptor()) .build(); 原理及涉及知识详解 作为Android开发,说到OKHttp的Interceptor,肯定熟悉不过了。那么你对 Interceptor 又了解多少呢?你都使用过那些OKHttp的 Interceptor呢? 我们先来看一下最近滴滴很火的哆啦A梦 DoraemonKit 长下面这个样子 [外链

m0_64319000的博客 652

Okhttp之五种拦截器

是整个责任链中的第一个,是首次接触到Request与最后接收到 Response的,主要功能就是判断是否需要重试与重定向。重试的前提是出现了 RouteException 或IOException。一旦在后续的拦截器执行过程中出现这两个异常,就会通过 recover 方法进行判断是否进行连接重试。重定向发生在重试的判定之后,如不满足重试的条件,还需要进一步调用 followUpRequest 根据Response 的响应码(如果直接请求失败, Response都不存在就会抛出异常)。

追梦的鱼儿 7131

基于Frida实现OKHttp的抓包

前言 在进行逆向分析时候,抓包有很多种方式,今天带来了一种新方法对基于OKHttp实现的网络请求进行抓包,该方法有一定的局限性。 Frida Frida是一款轻量级的Hook框架,Java与Native层只需几行代码即可进行Hook,本文就是基于该Hook框架实现。官方网站:https://frida.re/docs/home/ 原理 熟悉Android开发的应该知道,okhttp中有一个重要的设计那就是拦截器。通过拦截器可以监测到所有关于连接的信息。本文就是在原有拦截器上添加自己的拦截器进行网络抓包 F

Mrack 7769

OKHttp五大拦截器

文章目录【1】五大拦截器总体概述一、责任链设计模式①定义:②为什么要使用责任链模式【2】拦截器的工作流程一、默认的5大拦截器有哪些?【3】RetryAndFollowUpInterceptor拦截器一、重试二、重定向【4】BridgeInterceptor 桥接拦截器【5】CacheInterceptor缓存拦截器【6】ConnectInterceptor连接拦截器【7】CallServerInterceptor请求服务连接器【8】总结 【1】五大拦截器总体概述 OkHttp最核心的工作是在 getResp

zjcoming 7217

Android OkHttp+Retrofit+RxJava搭建网络访问框架(含源码)

网上高级工程师面试相关文章鱼龙混杂,要么一堆内容,要么内容质量太浅, 鉴于此我整理了上述安卓开发高级工程师面试题以及答案。希望帮助大家顺利进阶为高级工程师。目前我就职于某大厂安卓高级工程师职位,在当下大环境下也想为安卓工程师出一份力,通过我的技术经验整理了面试经常问的题,答案部分是一篇文章或者几篇文章,都是我认真看过并且觉得不错才整理出来。大家知道高级工程师不会像刚入门那样被问的问题一句话两句话就能表述清楚,所以我通过过滤好文章来帮助大家理解。现在都说互联网寒冬,其实只要自身技术能力够强,咱们就不怕。

2401_84148974的博客 946

Android 第三方框架:网络:OkHttp:源码分析:拦截器

将传入的Interceptor对象放入OkHttpClient.interceptors中OkHttpClient.interceptors最早添加到Interceptor的责任链中被添加到所有系统定义的Interceptor之前。

liufeismart2024的博客 769

【安卓开发系列 -- APP 开源框架】网络请求框架 OKHTTP -- 拦截器

【安卓开发系列 -- APP 开源框架】网络请求框架 OKHTTP -- 拦截器 【1】OKHTTP 拦截器概述 【1.1】拦截器中的请求(Request)与响应(Response)类分析 public final class Request { // 请求地址 final HttpUrl url; // 请求方法 final String method; // 请求头 final Headers headers; // 请求体 final @Nullable Req

CopperSun 技术博客 585

Android面试分析一:关于OKhttp详解(附带视频教程,flutter游戏全屏

面试专题OKhttp详解2 面试专题OKhttp详解终结篇 http协议 第一行:请求行 请求行下面的:请求属性集 问题一:选择网络访问框架的时候,为什么要选择OkHttp而不是其他框架; 明确一点: 并不期待,你将市面上所有的框架都全部搞得非常清楚,优缺点全部列出来;你是否具备掌控网络访问框架的能力; 这个问题没有标准答案,最好是带点主观意识 OkHttp XUtil 支持网络请求,图片加载,甚至还能操作数据库;就我个人而言,我认为,一个好的网络访问框架应该只专注一件事 Ret

m0_66684841的博客 4590

OkHttp源码分析

由Square公司贡献的一个处理网络请求的开源项目,是安卓目前使用最广泛的网络框架。从Android4.4开始HttpURLConnection的底层实现使用的是okhttp。

heheda123a的博客 393

Android OkHttp+Retrofit+RxJava搭建网络访问框架(含源码)(1)

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示。《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!return img;return wp;return xr;return cr;

2401_83817122的博客 1138

Android网络框架OkHttp源码分析(1)——请求流程

Android网络框架——OkHttp源码分析 前言 OkHttp是一个处理网络请求的高性能框架,由Square公司贡献。 它的出现替代了HttpUrlConnection和Apache HttpClient。 OkHttp采用了分层设计的思想,使用多层拦截器,每个拦截器解决一个问题,多层拦截器套在一起,就像设计模式中的装饰者模式一样,可以在保证每层功能高内聚的情况下,解决多样性的问题。 OkHttp源码的讲解可以分为两大部分: 请求流程 拦截器分析 接下来讲解第一部分: okhttp3源码分析之请求流

AthonyDavis的博客 578

Android网络框架OkHttp源码分析(2)——拦截器分析

Android网络框架——OkHttp源码分析 本文基于Kotlin语言讲解 上一篇文章: Android网络框架OkHttp源码分析(1)——请求流程 讲解了OkHttp的请求流程,这篇文章进入第二部分——OkHttp的拦截器分析。 OkHttp源码分析之拦截器分析 上一章节中发送请求的过程中有一个重要的方法getResponseWithInterceptorChain(),里面会创建许多拦截器,它们会在请求发送到服务器之前对请求做一系列的处理,在服务器返回响应之后,同样这些拦截器对响应做了一系列处理

AthonyDavis的博客 362

Android OkHttp+Retrofit+RxJava搭建网络访问框架(含源码),双非渣本Android四年磨一剑

外链图片转存中…(img-HdQUD2pI-1712341000871)][外链图片转存中…(img-udUr4TJF-1712341000871)][外链图片转存中…(img-vx1TH77G-1712341000871)][外链图片转存中…(img-Wfb5kDum-1712341000872)][外链图片转存中…(img-J0juAjIE-1712341000872)]

2301_77110865的博客 1001

Android 网络请求框架okhttp学习笔记

——你可以失望,但不能绝望。累的时候可以慢一点,千万不要后退,你还没有拼劲全力。怎么知道没有奇迹。 五、内容推荐 《优快云》《简书》 《Android Jetpack架构组件之Navigation入门》 《Android Jetpack架构组件之Room入门及源码分析》 《Android Jetpack架构组件之ViewModel入门到精通》 《Android Jetp...

斌林诚上的博客 885

宜搭子表单(自定义页面全部展示)

效果:增加自定义页面添加表格组件,配置数据源变量: formUuid'表单编码'变量: pageSize10变量: currentPage1。

2301_80286061的博客 2277

Cadence Allegro 17.4学习记录开始02-原理图Capture CIS 17.4

Allegro的第一方网表与第三方网表有以下几个区别点;Ø 与Allegro实现交互式操作的是第一方网表,第三方网表时不可以实现交互式操作;Ø 第三方网表不能将器件的Value属性导入到PCB中,输出时以封装属性来代替Value属性,第一方网表是可以的;Ø 网表导入到PCB中时,第三方的网表需要指定事先指定好PCB封装库文件,并产生Device文件,才可以将网表导入到PCB中,第一方网表则可以直接导入。

qq_31444421的博客 1万+
上一篇: Android网络框架——OKHttp
下一篇: Kotlin协程详解——拦截器ContinuationInterceptor
闲暇部落
博客等级 码龄12年 2776粉丝 215原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲暇部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值