如何同时使用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
相关产品推荐
相关产品推荐

