拦截器的作用与分类

1. 拦截器到底干了啥?

拦截器是 OkHttp 的“管道工”。请求从发起到返回,中间经过一道一道的拦截器。每个拦截器都可以看一眼请求、改一改请求、记录一下日志、甚至直接返回一个假的响应。我个人习惯把拦截器想象成“链式守卫”——每过一个关卡,你可以塞点私货进去。

举个例子,在项目中我需要统一添加 Authorization 请求头。如果没拦截器,你得在每个请求的构建代码里手动加,能加到你崩溃。有了拦截器,一行代码全搞定。这就是拦截器的核心价值:拦截与修改。

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(chain -> {
        Request request = chain.request();
        Request newRequest = request.newBuilder()
            .header("Authorization", "Bearer my_")
            .build();
        return chain.proceed(newRequest);
    })
    .build();

2. 两种拦截器:Application Interceptor vs Network Interceptor

OkHttp 里有两种拦截器,很多人刚接触时傻傻分不清:addInterceptor() 和 addNetworkInterceptor()。说白了,它们被调用的时机不同,用途也不同。

Application Interceptor(应用拦截器)

它位于最外层,只调用一次。即使发生了重定向或重试,它也只看到最初的请求和最终的响应。我在项目中常在应用拦截器里做统一加解密、打印请求日志——因为不看内部细节,只看业务结果。

Network Interceptor(网络拦截器)

它更贴近网络层,每一次真正的网络请求(包括重定向、重试)都会经过它。如果你想看真实的中间跳转、网络耗时、Cookie 变化,用网络拦截器更合适。我记得有一次排查请求超时问题,就是在网络拦截器里打印了每个重定向的 URL,才发现是某个跳转地址格式不对。

对比维度 Application Interceptor Network Interceptor
调用次数 只 1 次(不管重定向) 可能多次(每次网络请求都调用)
可访问的响应 最终响应(已处理重定向) 原始响应(含中间重定向)
典型用途 统一 header、日志、异常处理 重定向追踪、网络缓存、Cookie 注入
添加方法 addInterceptor() addNetworkInterceptor()
小经验:如果你只是想打个全量日志,用应用拦截器就够了。如果你想调试网络底层的跳转逻辑,用网络拦截器。我建议两个都加——但顺序很重要,下面讲。

3. 添加顺序与优先级

说到顺序,这里有个容易踩的坑。拦截器是按添加顺序执行的——越早添加的越先生效。但这只是表面,实际执行链更像一个洋葱结构:请求进去时先过应用拦截器,再过网络拦截器;响应回来时反着来,先过网络拦截器,再过应用拦截器。

为什么顺序这么关键?举个例子:我在一个项目里先加了网络拦截器用于打网络日志,后加了应用拦截器用于统一加 。结果发现 没被加到重定向后的请求里——因为应用拦截器只跑一次,而网络拦截器每次都跑,重定向时 早被处理掉了。这坑我踩过一次,好几天才想明白。

优先级其实就一句话:添加顺序决定了内外层关系。如果你想让一个拦截器总是最后处理请求(比如加签名),就最后添加;如果想让一个拦截器最底层的网络行为(比如重试),就最先添加。

// 举个例子:
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new LoggingInterceptor())       // 应用层,第1个添加 → 最外层
    .addInterceptor(new AuthInterceptor())          // 应用层
    .addNetworkInterceptor(new RetryInterceptor())  // 网络层
    .addNetworkInterceptor(new CookieInterceptor()) // 网络层
    .build();

上面这段代码的执行顺序是:请求 → Logging → Auth → (进入网络层) → Retry → Cookie → 真正的网络请求 → Cookie → Retry → (回到应用层) → Auth → Logging → 返回给调用方。

⚠ 注意:不要在同一个层级中依赖前后顺序来做条件判断,除非你非常确定当前版本 OkHttp 的内部实现。我曾经见到有人利用 addInterceptor 顺序来实现“取消某些请求缓存”,后来升级 OkHttp 版本后行为变了,线上直接崩了一大片。

4. 实战中的避坑指南

  • 不要在网络拦截器中修改请求方法/路径:如果重定向后的请求重新生成,可能导致循环重定向。我在项目里试过把 GET 改成 POST,结果无限 302 崩溃。
  • 应用拦截器里尽量不抛异常:如果抛出运行时异常,OkHttp 不会进行重试,整个请求链直接断。我建议用 catch 捕获并返回一个自定义错误响应。
  • 网络拦截器里慎用 close():响应体一旦关闭,上层就再也读不到了。我有次在调试时提前关闭了 Response body,结果应用拦截器拿到的数据为空,找了半天 bug。
拦截器执行流程与分类 请求 Application (应用拦截器) Network (网络拦截器) 网络 响应返回 Network 处理响应 Application 处理响应 返回 关键点 1. 添加顺序:越早添加的拦截器越外层(先执行请求,后执行响应)。 2. 应用拦截器只执行 1 次;网络拦截器可能执行多次(重定向/重试时)。 3. 如果你想做全局统一处理(如加 ),用应用拦截器并最后添加。 4. 如果涉及真正的网络行为(如重定向日志),用网络拦截器并最先添加。
一句话总结:Application Interceptor 管业务逻辑,Network Interceptor 管网络细节。顺序决定了内外层次,优先级取决于你的需求。别把两者混用、别随便乱调顺序,否则线上崩溃的锅就是你的了。