You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:22:19