Android Retrofit懒加载API缓存问题:1小时后才获取新数据
解决Retrofit请求缓存过旧的问题
问题根源分析
你的代码存在几个导致缓存异常的问题:
- OkHttpClient未配置缓存目录:没有指定缓存存储位置和大小,但如果服务器返回
Cache-Control: max-age=3600(1小时)的响应头,客户端会默认遵循该规则缓存数据。 - CacheInterceptor逻辑混乱:
- 同时设置
maxAge(60, TimeUnit.SECONDS)和noCache(),两个配置冲突,noCache会强制请求去服务器验证缓存,但最后又硬编码设置max-age=60的响应头,逻辑矛盾。 Log语句写在return之后,永远不会执行,无法调试缓存逻辑。
- 同时设置
- OkHttpClient构建冗余:代码中额外创建了一个未使用的OkHttpClient实例,可能导致配置不生效。
修复方案
方案1:全局禁用缓存(适合需实时数据的场景)
如果所有接口都需要实时数据,直接在拦截器中强制禁用缓存:
1. 修改CacheInterceptor
class CacheInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request().newBuilder() // 强制跳过缓存,直接请求服务器 .header("Cache-Control", "no-cache, no-store, must-revalidate") .header("Pragma", "no-cache") .header("Expires", "0") .build() return chain.proceed(request) } }
2. 修正OkHttpClient配置
移除冗余的构建代码,确保使用配置好的实例:
val okHttpClient: OkHttpClient = OkHttpClient.Builder() .connectTimeout(6, TimeUnit.SECONDS) .writeTimeout(6, TimeUnit.SECONDS) .readTimeout(6, TimeUnit.SECONDS) .addInterceptor(CacheInterceptor()) // 用应用拦截器,确保所有请求生效 .build() private val retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(okHttpClient) .build() } val api: SimpleApi by lazy { retrofit.create(SimpleApi::class.java) }
方案2:按需控制缓存(部分接口缓存,部分实时)
通过给接口添加自定义头,区分缓存策略:
1. 修改CacheInterceptor
class CacheInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() val customCacheHeader = request.header("Custom-Cache") val modifiedRequest = when (customCacheHeader) { "no-cache" -> { // 实时接口:禁用缓存 request.newBuilder() .header("Cache-Control", "no-cache, no-store, must-revalidate") .removeHeader("Custom-Cache") // 移除自定义头,避免传到服务器 .build() } else -> { // 缓存接口:设置短有效期(如30秒) request.newBuilder() .header("Cache-Control", "public, max-age=30") .build() } } val response = chain.proceed(modifiedRequest) // 替换响应中的缓存头,统一规则 return response.newBuilder() .removeHeader("Pragma") .removeHeader("Cache-Control") .header("Cache-Control", customCacheHeader ?: "public, max-age=30") .build() } }
2. 在API接口中指定策略
interface SimpleApi { // 实时数据接口 @Headers("Custom-Cache: no-cache") @GET("api/latest-data") suspend fun getLatestData(): Response<DataModel> // 允许缓存的接口 @GET("api/cached-data") suspend fun getCachedData(): Response<DataModel> }
方案3:配置缓存目录并控制缓存有效期
如果需要启用缓存但自定义有效期,必须先指定缓存存储位置:
// 在Application或初始化类中获取缓存目录 val cacheSize = 10 * 1024 * 1024 // 10MB缓存空间 val cache = Cache(context.cacheDir, cacheSize) val okHttpClient: OkHttpClient = OkHttpClient.Builder() .connectTimeout(6, TimeUnit.SECONDS) .writeTimeout(6, TimeUnit.SECONDS) .readTimeout(6, TimeUnit.SECONDS) .cache(cache) // 添加缓存配置 .addNetworkInterceptor { chain -> val response = chain.proceed(chain.request()) // 设置响应缓存有效期为30秒 val cacheControl = CacheControl.Builder() .maxAge(30, TimeUnit.SECONDS) .build() response.newBuilder() .header("Cache-Control", cacheControl.toString()) .removeHeader("Pragma") .build() } .build()
关键注意点
- 应用拦截器vs网络拦截器:
addInterceptor会拦截所有请求(包括缓存命中的请求);addNetworkInterceptor仅拦截发往服务器的请求,按需选择。 - 避免配置冲突:不要同时使用
noCache和maxAge,二者互斥。 - 调试验证:可以用Charles等工具查看请求/响应头,确认缓存规则是否生效。
内容的提问来源于stack exchange,提问作者John Chu
相关产品推荐
相关产品推荐

