第四章 添加缓存请求头:@Header、@Headers注解、动态Header
在Retrofit缓存策略中,请求头是控制缓存行为的关键载体。本章深入解析通过注解方式添加静态缓存头,以及利用动态Header实现灵活缓存策略的原理与实现流程。
4.1 原理概述
HTTP缓存头字段(如 Cache-Control、Expires、ETag)由客户端通过请求头发送给服务器,指示缓存策略。Retrofit 提供三种注入方式:
- @Header – 单个请求头,参数值固定或通过方法参数传入。
- @Headers – 多个请求头,直接写在方法上方。
- 动态Header – 通过方法参数或拦截器在运行时构造缓存头。
三种方式均可在OkHttp拦截层生效,与缓存拦截器配合实现精细化缓存。
核心原则: 请求头的优先级为:动态Header > @Header参数 > @Headers注解 > 默认请求头。
4.2 实现流程
- 定义接口 – 在Retrofit service接口中使用
@Headers或@Header声明静态缓存头。 - 参数传递 – 对于动态值,在方法参数中使用
@Header("Cache-Control")。 - 运行时构造 – 动态Header可通过拦截器或OkHttp的
Request.Builder二次注入。 - 合并生效 – 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 参数从安全存储中动态传入。