Glide 4如何对比远程与本地文件的Last-Modified请求头?
在Glide 4中,不推荐像UniversalImageLoader那样手动操作缓存文件(因为Glide的缓存结构是经过哈希处理的,直接查找或删除缓存容易破坏Glide的缓存逻辑),更符合Glide设计的最优方式是利用Signature(签名)机制来关联远程文件的Last-Modified时间,让Glide自动判断是否需要更新缓存。下面分步骤给你详细实现方案:
1. 核心思路:用Last-Modified作为Glide的Signature
Glide的缓存键由请求模型(比如图片URL)和Signature共同决定。当远程文件的Last-Modified更新时,我们同步更新Signature,Glide就会认为这是一个新资源,自动重新下载并替换旧缓存,旧缓存会被Glide的LRU策略自动清理,无需手动删除。
2. 具体代码实现
第一步:封装获取远程Last-Modified的方法
和你之前UIL的逻辑类似,通过HEAD请求获取远程文件的Last-Modified时间:
private long getRemoteLastModified(String imageUrl) { HttpURLConnection connection = null; try { URL url = new URL(imageUrl); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("HEAD"); connection.setConnectTimeout(5000); connection.setReadTimeout(5000); connection.connect(); return connection.getLastModified(); } catch (IOException e) { e.printStackTrace(); return 0; // 出错时返回0,后续会用默认缓存逻辑 } finally { if (connection != null) { connection.disconnect(); } } }
第二步:加载图片时绑定Signature
获取远程Last-Modified后,用ObjectKey(Glide提供的通用Signature实现)创建签名,然后传入Glide请求:
// 获取远程文件的Last-Modified时间 long remoteLastModified = getRemoteLastModified(iconUrl); // 加载图片,绑定Signature Glide.with(context) .load(iconUrl) // 用远程Last-Modified作为签名,时间变化则缓存失效 .signature(new ObjectKey(remoteLastModified)) .listener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { // 处理加载失败逻辑,比如显示占位图 return false; // 返回false让Glide继续处理默认失败逻辑 } @Override public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { // 当资源从远程下载时,说明缓存已更新 if (dataSource == DataSource.REMOTE) { Log.d(Appspress.LOG_TAG_DEBUG, "old picture detected. Redownloaded new version."); } return false; // 返回false让Glide继续处理资源显示 } }) .into(imageView);
3. 性能优化:缓存远程Last-Modified时间
上面的方案每次加载都要发HEAD请求,可能会增加网络开销。你可以把远程Last-Modified时间缓存到本地(比如SharedPreferences),设置一个过期时间(比如10分钟),过期后再重新请求:
private static final long LAST_MODIFIED_CACHE_DURATION = 10 * 60 * 1000; // 10分钟 private long getCachedRemoteLastModified(String imageUrl) { SharedPreferences sp = context.getSharedPreferences("ImageLastModified", Context.MODE_PRIVATE); long cachedTime = sp.getLong(imageUrl, 0); long cacheTimestamp = sp.getLong(imageUrl + "_timestamp", 0); // 如果缓存未过期,返回缓存的时间 if (System.currentTimeMillis() - cacheTimestamp < LAST_MODIFIED_CACHE_DURATION) { return cachedTime; } // 缓存过期,重新获取并更新缓存 long newTime = getRemoteLastModified(imageUrl); sp.edit() .putLong(imageUrl, newTime) .putLong(imageUrl + "_timestamp", System.currentTimeMillis()) .apply(); return newTime; }
然后加载时替换为getCachedRemoteLastModified(iconUrl)即可。
4. 进阶优化:结合OkHttp自动处理缓存验证
如果你使用OkHttp作为Glide的网络库,可以配置拦截器自动发送If-Modified-Since请求头,让服务器判断是否需要返回新资源(返回304则复用本地缓存),这样无需提前发HEAD请求,更高效:
第一步:添加OkHttp集成依赖
在build.gradle中添加:
implementation 'com.github.bumptech.glide:okhttp3-integration:4.15.1'
第二步:配置OkHttp拦截器
OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); String imageUrl = originalRequest.url().toString(); // 从SharedPreferences获取本地缓存的Last-Modified SharedPreferences sp = context.getSharedPreferences("ImageLastModified", Context.MODE_PRIVATE); long localLastModified = sp.getLong(imageUrl, 0); // 添加If-Modified-Since请求头 Request modifiedRequest = originalRequest.newBuilder() .header("If-Modified-Since", new Date(localLastModified).toString()) .build(); Response response = chain.proceed(modifiedRequest); // 如果返回200,说明资源更新了,保存新的Last-Modified if (response.code() == HttpURLConnection.HTTP_OK) { Date lastModifiedDate = response.headers().getDate("Last-Modified"); if (lastModifiedDate != null) { sp.edit() .putLong(imageUrl, lastModifiedDate.getTime()) .apply(); } } return response; } }) .build();
第三步:让Glide使用自定义OkHttpClient
// 替换Glide默认的网络加载器为OkHttp GlideUrlLoader.Factory factory = new GlideUrlLoader.Factory(okHttpClient); Glide.get(context).getRegistry().replace(GlideUrl.class, InputStream.class, factory);
这种方式下,Glide会自动处理缓存验证,无需手动设置Signature,服务器返回304时直接复用本地缓存,返回200时自动更新缓存和本地存储的Last-Modified。
总结
- 最推荐的基础方案是使用Signature关联远程Last-Modified,无需手动操作缓存,符合Glide的设计规范。
- 性能优化可以加上本地缓存Last-Modified时间,减少HEAD请求次数。
- 进阶方案结合OkHttp拦截器,实现自动缓存验证,进一步节省带宽和提升加载效率。
内容的提问来源于stack exchange,提问作者Gleb

