Android:使用Glide预加载图片时如何取消图片请求?
RecyclerView无限列表图片预加载+未完成请求取消实现方案
嘿,这个需求我之前做无限滚动列表的时候正好碰到过,给你一套落地性很强的实现方案,一步步来:
一、核心思路概述
核心就是用一个位置-请求的映射表(Map)来跟踪每个item的图片加载请求:
- 在绑定ViewHolder时,除了加载当前item的图片,还主动触发后面3个item的图片预加载,同时把请求存入Map
- 当ViewHolder从屏幕上分离时,去Map里找对应位置的请求,如果还没完成就取消它,并且从Map中移除,避免资源浪费和内存泄漏
二、具体实现步骤
1. 先定义请求管理容器
在你的RecyclerView Adapter里声明一个Map,用来存每个位置的图片加载请求。这里假设你用OkHttp做网络请求,要是用Glide/Picasso这类图片库,换成对应的请求对象就行:
// Adapter类里的成员变量 private final Map<Integer, Call> imageRequestMap = new HashMap<>();
2. 预加载逻辑(在onBindViewHolder里处理)
绑定ViewHolder的时候,先加载当前item的图片,然后循环处理后面3个位置的预加载,注意要判断位置是否在数据范围内,别越界:
@Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { // 先处理当前item的图片加载 loadCurrentItemImage(holder, position); // 预加载后面3个item的图片 int preloadCount = 3; for (int i = 1; i <= preloadCount; i++) { int preloadPos = position + i; // 检查是否在有效数据范围内,且该位置还没有发起过请求 if (preloadPos < getItemCount() && !imageRequestMap.containsKey(preloadPos)) { Call request = startPreloadImageRequest(preloadPos); imageRequestMap.put(preloadPos, request); } else if (preloadPos >= getItemCount()) { // 已经到列表末尾了,直接跳出循环 break; } } }
这里的startPreloadImageRequest是你自己封装的图片加载方法,需要返回可以取消的请求实例(比如OkHttp的Call),同时要在请求完成(成功/失败)后从Map里移除条目,这个后面会说。
3. 分离时取消未完成请求(onViewDetachedFromWindow)
当ViewHolder滚出屏幕时,触发这个方法,我们在这里检查对应位置的请求状态,没完成就取消:
@Override public void onViewDetachedFromWindow(@NonNull MyViewHolder holder) { super.onViewDetachedFromWindow(holder); int position = holder.getAdapterPosition(); // 先判断位置是否有效,避免返回NO_POSITION的情况 if (position == RecyclerView.NO_POSITION) { return; } // 从Map里拿对应位置的请求 Call request = imageRequestMap.get(position); if (request != null) { // 检查请求是否还没完成/取消,如果是就取消它 if (!request.isCanceled() && !request.isExecuted()) { request.cancel(); } // 不管取消成功与否,都从Map里移除,避免占用内存 imageRequestMap.remove(position); } }
4. 请求完成后的清理
不管图片加载成功还是失败,都要把对应的请求从Map里删掉,不然Map会越来越大,导致内存问题:
private Call startPreloadImageRequest(int position) { String imageUrl = getImageUrlByPosition(position); // 你自己的获取图片URL的方法 Call call = new OkHttpClient().newCall(new Request.Builder() .url(imageUrl) .build()); call.enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { // 请求失败,移除Map条目 imageRequestMap.remove(position); // 这里可以加失败日志或者重试逻辑 } @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { // 请求成功,移除Map条目 imageRequestMap.remove(position); // 这里可以把图片缓存起来,等ViewHolder绑定的时候直接用 } }); return call; }
三、如果用Glide/Picasso这类图片库的适配
要是你用Glide,其实它本身有请求管理,但我们还是可以手动跟踪预加载请求,调整一下代码就行:
// 换成Glide的Request作为Map的value private final Map<Integer, Request> imageRequestMap = new HashMap<>(); private Request startPreloadImageRequest(int position, Context context) { String imageUrl = getImageUrlByPosition(position); // 用submit()方法获取Request,适合预加载(不需要立即显示到ImageView) Request request = Glide.with(context) .load(imageUrl) .onlyRetrieveFromCache(false) // 强制走网络,避免缓存影响预加载效果 .addListener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { imageRequestMap.remove(position); return false; // 让Glide继续处理失败逻辑 } @Override public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { imageRequestMap.remove(position); return false; // 让Glide继续处理成功逻辑 } }) .submit(); return request; } // 取消请求的逻辑调整 @Override public void onViewDetachedFromWindow(@NonNull MyViewHolder holder) { super.onViewDetachedFromWindow(holder); int position = holder.getAdapterPosition(); if (position == RecyclerView.NO_POSITION) { return; } Request request = imageRequestMap.get(position); if (request != null && !request.isComplete()) { request.clear(); // Glide的取消方法 imageRequestMap.remove(position); } }
四、几个要注意的细节
- 一定要判断
getAdapterPosition()是否为NO_POSITION,因为ViewHolder可能已经被回收,位置无效了 - 预加载前要检查Map里是否已有对应位置的请求,避免快速滚动时重复发起请求,浪费带宽
- 如果列表有数据更新(比如下拉刷新、上拉加载更多),记得清空Map里的旧请求,避免引用过时的请求对象
- 要是担心Map内存泄漏,可以考虑用
WeakHashMap,但要注意它的特性:当key(位置)没有其他引用时,会被自动回收
内容的提问来源于stack exchange,提问作者thedarkpassenger
相关产品推荐
相关产品推荐

