第四章 添加缓存请求头:@Header、@Headers注解、动态Header

在Retrofit缓存策略中,请求头是控制缓存行为的关键载体。本章深入解析通过注解方式添加静态缓存头,以及利用动态Header实现灵活缓存策略的原理与实现流程。

4.1 原理概述

HTTP缓存头字段(如 Cache-Control、Expires、ETag)由客户端通过请求头发送给服务器,指示缓存策略。Retrofit 提供三种注入方式:

  • @Header – 单个请求头,参数值固定或通过方法参数传入。
  • @Headers – 多个请求头,直接写在方法上方。
  • 动态Header – 通过方法参数或拦截器在运行时构造缓存头。

三种方式均可在OkHttp拦截层生效,与缓存拦截器配合实现精细化缓存。

核心原则: 请求头的优先级为:动态Header > @Header参数 > @Headers注解 > 默认请求头。
缓存请求头注入流程 Retrofit 接口 @Headers 编译期注入 @Header 方法参数 动态Header OkHttp Request (合并头) 优先级顺序:动态Header > @Header参数 > @Headers注解

4.2 实现流程

  1. 定义接口 – 在Retrofit service接口中使用 @Headers 或 @Header 声明静态缓存头。
  2. 参数传递 – 对于动态值,在方法参数中使用 @Header("Cache-Control")。
  3. 运行时构造 – 动态Header可通过拦截器或OkHttp的 Request.Builder 二次注入。
  4. 合并生效 – OkHttp在发送前合并所有头域,最终请求头包含缓存指令。

4.3 核心API与代码示例

4.3.1 @Headers 注解(编译期静态头)

public interface CacheApi {
    @Headers({
        "Cache-Control: max-age=3600",
        "Pragma: no-cache"
    })
    @GET("user/profile")
    Call<User> getProfile();
}

4.3.2 @Header 参数头(方法参数动态头)

public interface CacheApi {
    @GET("user/profile")
    Call<User> getProfile(
        @Header("Cache-Control") String cacheControl
    );
}

// 调用时
Call<User> call = api.getProfile("max-age=0, no-cache");

4.3.3 动态Header通过拦截器实现

public class DynamicCacheHeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request.Builder builder = originalRequest.newBuilder();

        // 动态添加缓存请求头
        if (isOnline()) {
            builder.header("Cache-Control", "max-age=60");
        } else {
            builder.header("Cache-Control", "only-if-cached, max-stale=604800");
        }

        Request modifiedRequest = builder.build();
        return chain.proceed(modifiedRequest);
    }

    private boolean isOnline() {
        // 实际网络状态检测逻辑
        return true;
    }
}
调试建议: 使用OkHttp的日志拦截器 HttpLoggingInterceptor 打印最终请求头,验证缓存头是否正确注入。

4.4 调试与验证

在Android Studio中运行抓取日志,观察请求头中的 Cache-Control 值:

D/OkHttp: --> GET http://api.example.com/user/profile
D/OkHttp: Cache-Control: max-age=60
D/OkHttp: Pragma: no-cache
D/OkHttp: --> END GET

4.5 常见部署错误

错误现象 原因 解决方案
Header未生效 拦截器顺序错误,缓存拦截器在Header注入之前。 确保自定义拦截器添加到OkHttp的 addInterceptor 列表中。
动态Header覆盖了静态头 未按优先级规则设计,或拦截器中重复设置相同头域。 检查拦截器中是否误用了 addHeader(追加)或 header(覆盖)。
参数类型不匹配 @Header参数传入 null 导致Header被移除。 传入空字符串 "" 或使用 @Nullable 并处理空值。

4.6 性能与稳定性优化

  • 减少不必要的Header – 避免在每一个请求中重复添加静态头,使用 @Headers 设定公共头。
  • 动态Header缓存 – 如果动态头计算逻辑较重(如加密签名),可将其结果缓存在内存中,避免重复计算。
  • 避免在拦截器中频繁修改 – 对于变化频率极低的Header(如设备ID),应优先使用 @Header 参数而非拦截器。
  • 测试头域长度 – 某些Web服务器对请求头长度有限制,动态Header过长时应考虑压缩或拆分。
注意: 不要在 @Headers 中设置敏感信息(如),因为注解值在编译时即为字符串常量,可能被反编译泄露。敏感头应使用 @Header 参数从安全存储中动态传入。