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

如何同时使用DiskLruCache与LruCache开发图片加载器?

双缓存图片加载器实现方案与源码分享

我目前正在开发一款结合内存缓存和磁盘缓存的图片加载器,为了兼顾加载速度和离线可用性,梳理出了一套核心加载逻辑,下面是详细逻辑和对应的源码片段:

核心加载流程

  • 应用启动阶段:先从磁盘缓存中加载已存储的图片,保证离线状态下也能快速显示历史图片
  • 内存缓存优先检查:每次加载图片时,先判断目标图片是否在内存缓存中——内存读取速度最快,能最大程度提升用户体验,存在的话直接加载
  • 磁盘缓存兜底同步:如果内存缓存没有目标图片,就去检查磁盘缓存;找到后立刻把图片存入内存缓存,方便后续再次请求时直接走内存
  • 网络加载 fallback:当内存和磁盘缓存都找不到目标图片时,通过AsyncTask从指定URL下载并生成Bitmap,下载完成后将图片存入磁盘缓存,下次启动就能直接读取

关键源码片段(Application的onCreate()及核心方法)

注:以下是我实现的核心代码部分,主要集中在Application的初始化和图片加载逻辑:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 初始化磁盘缓存,创建缓存目录并设置缓存上限
    initDiskCache();
    // 初始化内存缓存,采用LruCache实现内存自动回收
    initMemoryCache();
    // 预加载首页等常用图片到内存缓存,提升启动后首次加载速度
    preloadCommonImages();
}

// 内存缓存初始化:用LruCache限制内存占用,避免OOM
private void initMemoryCache() {
    // 取应用可用内存的1/8作为缓存上限,平衡性能和内存占用
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    int cacheSize = maxMemory / 8;
    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // 计算单张Bitmap占用的内存大小(转成KB单位)
            return bitmap.getByteCount() / 1024;
        }
    };
}

// 磁盘缓存初始化:用DiskLruCache实现持久化缓存
private void initDiskCache() {
    // 优先取外部存储缓存目录,空间不足则用内部缓存目录
    File cacheDir = getExternalCacheDir();
    if (cacheDir == null) {
        cacheDir = getCacheDir();
    }
    // 设置磁盘缓存上限为50MB,避免占用过多存储空间
    long diskCacheSize = 50 * 1024 * 1024;
    try {
        mDiskCache = DiskLruCache.open(cacheDir, 1, 1, diskCacheSize);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// 图片加载核心入口方法
public Bitmap loadBitmap(String imageUrl) {
    String key = generateKeyFromUrl(imageUrl);
    // 第一步:检查内存缓存
    Bitmap bitmap = mMemoryCache.get(key);
    if (bitmap != null) {
        return bitmap;
    }
    // 第二步:内存缓存无,检查磁盘缓存
    bitmap = loadBitmapFromDiskCache(key);
    if (bitmap != null) {
        // 将磁盘缓存的图片存入内存缓存,加速后续访问
        mMemoryCache.put(key, bitmap);
        return bitmap;
    }
    // 第三步:缓存都没有,异步从网络加载
    new BitmapLoadTask().execute(imageUrl);
    return null;
}

// 异步加载网络图片的任务
private class BitmapLoadTask extends AsyncTask<String, Void, Bitmap> {
    @Override
    protected Bitmap doInBackground(String... params) {
        String imageUrl = params[0];
        String key = generateKeyFromUrl(imageUrl);
        Bitmap bitmap = null;
        try {
            // 从URL获取输入流,解码生成Bitmap
            InputStream is = new URL(imageUrl).openStream();
            bitmap = BitmapFactory.decodeStream(is);
            // 将生成的Bitmap存入磁盘缓存
            saveBitmapToDiskCache(key, bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        if (bitmap != null) {
            String key = generateKeyFromUrl(imageUrl);
            // 加载完成后存入内存缓存
            mMemoryCache.put(key, bitmap);
            // 更新对应ImageView显示图片
            updateImageView(bitmap);
        }
    }
}

// 辅助方法:将URL转换成唯一缓存Key(比如用MD5加密)
private String generateKeyFromUrl(String url) {
    try {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        digest.update(url.getBytes());
        return bytesToHexString(digest.digest());
    } catch (NoSuchAlgorithmException e) {
        return String.valueOf(url.hashCode());
    }
}

// 字节数组转十六进制字符串的辅助方法
private String bytesToHexString(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte aByte : bytes) {
        String hex = Integer.toHexString(0xFF & aByte);
        if (hex.length() == 1) {
            sb.append('0');
        }
        sb.append(hex);
    }
    return sb.toString();
}

内容的提问来源于stack exchange,提问作者Adrian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:55:41