一、概述
在分析OkHttp3的缓存机制之前先手写一个实现了三级缓存的ImageLoader来整体感受一下LruCache和DiskLruCache的用法。本例实现了三级缓存,利用LruCache实现内存缓存,利用DiskLruCache实现磁盘缓存。整体的流程是:当用户请求一张图时,首先检查内存中是否有缓存图片,如果有就直接返回,如果没有就检查磁盘中是否有,有就返回,没有就启用网络下载图片,然后把图片缓存在磁盘缓存和内存缓存中。下面看看具体的实现步骤。
二、源程序介绍
1.ImageLoader.java:是一个单例类,是加载图片的入口.
2.CacheManager.java用来管理LruCacheManger和DiskLruCacheManager
3.LruCacheManager.java是用来管理内存缓存的。
4.DiskLruCacheManager是用来管理磁盘缓存的
5.DownloadFileUtil.java是用来下载文件的,本例指的是图片
6.MainThreadHandler.java是创建了一个运行在主线程中的Handler
7.Util.java工具类
8.MsgConfig.java配置类
9.LoaderResult.java存储view、bitmap和uri的工具类
10.ImageResizer.java用来缩放图片用的
整体类图结构
ImageLoader.java中就放了一个线程池(自定义线程池)外加displayView显示方法
public class ImageLoader { private ImageLoader(Context context) { mainThreadHandler = new MainThreadHandler(); cacheManager = new CacheManager(context, THREAD_POOL_EXECUTOR, mainThreadHandler); } private static final String TAG = "ImageLoader"; public static final int MESSAGE_POST_RESULT = 1; //核心Cpu数量 private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1;//核心线程数量 //线程池的最大容量 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final long KEEP_ALIZE = 10l; private static final ThreadFactory threadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "ImageLoader#" + mCount.getAndIncrement()); } }; //创建一个线程池 public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIZE, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(), threadFactory); private Context context; private CacheManager cacheManager; private MainThreadHandler mainThreadHandler; private static ImageLoader imageLoader = null; public static ImageLoader getDefault(Context context) { synchronized (ImageLoader.class){ if(imageLoader==null){ imageLoader = new ImageLoader(context); } } return imageLoader; } /** * 异步加载一张图片并显示在view上 * * @param url * @param view */ public void displayView(String url, View view) { cacheManager.bindBitmap(url, view); } /** * 根据url获取一个bitmap * * @param url * @param reqWidth * @param reqHeight * @return */ public Bitmap getBitmap(String url, int reqWidth, int reqHeight) { return cacheManager.loadBitmap(url, reqWidth, reqHeight); } /** * 异步加载指定图片的大小加载图片 * * @param url * @param view * @param reqWidth * @param reqHeight */ public void displayView(String url, View view, int reqWidth, int reqHeight) { cacheManager.bindBitmap(url, view, reqWidth, reqHeight); } }
CacheManager.java用来管理磁盘缓存 和内存缓存,并实现三级缓存的判断
package com.yw.library; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Build; import android.os.Looper; import android.os.StatFs; import android.util.LruCache; import android.view.View; import android.widget.ImageView; import com.jakewharton.disklrucache.DiskLruCache; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.OutputStream; import java.security.MessageDigest; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; /** * 缓存管理类 * create by yangwei * on 2020-03-01 22:13 */ public class CacheManager { private ImageResizer imageResizer = new ImageResizer(); private Executor THREAD_POOL_EXECUTOR; //磁盘缓存的最大容量 public static final long DISK_CACHE_SIZE = 1024 * 1024 * 50; public static final int DISK_CACHE_INDEX = 0; private LruCacheManager lruCacheManager; private DiskLruCacheManager diskLruCacheManager; private MainThreadHandler mainThreadHandler; private Context context; public CacheManager(Context context,Executor THREAD_POOL_EXECUTOR,MainThreadHandler mainThreadHandler) { this.THREAD_POOL_EXECUTOR = THREAD_POOL_EXECUTOR; this.mainThreadHandler = mainThreadHandler; this.context = context; lruCacheManager = new LruCacheManager(); diskLruCacheManager = new DiskLruCacheManager(context,imageResizer,lruCacheManager); } public void bindBitmap(final String uri, final View view) { bindBitmap(uri, view, 0, 0); } public void bindBitmap(final String uri, final View view, final int reqWidth, final int reqHeight) { view.setTag(MsgConfig.TAG_KEY_URI, uri); Bitmap bitmap = loadBitmapFromMemoryCache(uri); if (bitmap != null) { if (view instanceof ImageView) { ((ImageView) view).setImageBitmap(bitmap); } else { view.setBackground(new BitmapDrawable(bitmap)); } return; } //利用线程池在后台加载图片,加载成功后进入主线程更新UI THREAD_POOL_EXECUTOR.execute(new Runnable() { @Override public void run() { Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight); LoaderResult loaderResult = new LoaderResult(view, uri, bitmap); //向主线程中发送loaderresult mainThreadHandler.obtainMessage(MsgConfig.MESSAGE_POST_RESULT, loaderResult).sendToTarget(); } }); } public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) { Bitmap bitmap = loadBitmapFromMemoryCache(uri); if (bitmap != null) { return bitmap; } bitmap = diskLruCacheManager.loadBitmapFromDiskCache(uri, reqHeight, reqHeight); if (bitmap != null) { return bitmap; } bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight); if (bitmap != null) { return bitmap; } if (bitmap == null && !diskLruCacheManager.mIsDiskLruCacheCreated) { bitmap = DownloadFileUtil.get().downloadBitmapFromUrl(uri); } return bitmap; } private Bitmap loadBitmapFromMemoryCache(String url) { final String key = Util.hashKeyFromUrl(url); Bitmap bitmap = lruCacheManager.getBitmapFromMemoryCache(key); return bitmap; } private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight) { diskLruCacheManager.addBitmapToDiskCache(url); return diskLruCacheManager.loadBitmapFromDiskCache(url, reqWidth, reqHeight); } public static class Builder { public Builder(){} } }
LruCacheManager.java用来管理LruCache
package com.yw.library; import android.graphics.Bitmap; import android.util.LruCache; /** * create by yangwei * on 2020-03-01 22:30 */ public class LruCacheManager { private LruCache<String, Bitmap> mMemoryCache = null; public LruCacheManager() { int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { //返回bitmap的大小,单位是kb return value.getRowBytes() * value.getHeight() / 1024; } }; } /** * 相内存缓存中添加一个Bitmap * * @param key * @param bitmap */ public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemoryCache(key) == null) { mMemoryCache.put(key, bitmap); } } /** * 从内存缓存中取出一个Bitmap * * @param key * @return */ public Bitmap getBitmapFromMemoryCache(String key) { return mMemoryCache.get(key); } }
DissLruCacheManager用来管理DiskLruCache
package com.yw.library; import android.content.Context; import android.graphics.Bitmap; import android.os.Build; import android.os.Looper; import android.os.StatFs; import com.jakewharton.disklrucache.DiskLruCache; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.OutputStream; /** * 磁盘缓存管理 * create by yangwei * on 2020-03-01 22:30 */ public class DiskLruCacheManager { private DiskLruCache mDiskLruCache = null; private Context context; public boolean mIsDiskLruCacheCreated = false; private ImageResizer imageResizer; private LruCacheManager lruCacheManager; /** * 初始化参数 * * @param context * @param imageResizer * @param lruCacheManager */ public DiskLruCacheManager(Context context, ImageResizer imageResizer, LruCacheManager lruCacheManager) { this.context = context; this.imageResizer = imageResizer; this.lruCacheManager = lruCacheManager; //创建磁盘缓存路径 File diskCacheDir = FileUtil.createDiskCacheDir(context, "bitmap"); if (!diskCacheDir.exists()) { diskCacheDir.mkdirs(); } if (getUsableSpace(diskCacheDir) > CacheManager.DISK_CACHE_SIZE) { try { mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, CacheManager.DISK_CACHE_SIZE); mIsDiskLruCacheCreated = true; } catch (Exception e) { e.printStackTrace(); } } } /** * 添加Bitmap到磁盘 * * @param url */ public void addBitmapToDiskCache(String url) { if (Looper.myLooper() == Looper.getMainLooper()) { throw new RuntimeException("can not visit network form Ui Thread"); } try { String key = Util.hashKeyFromUrl(url); DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(CacheManager.DISK_CACHE_INDEX); if (DownloadFileUtil.get().downloadUrlToStream(url, outputStream)) { editor.commit(); } else { editor.abort(); } mDiskLruCache.flush(); } } catch (Exception e) { e.printStackTrace(); } } /** * 从磁盘中加载图片 * * @param url * @param reqWidth * @param reqHeight * @return */ public Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight) { if (Looper.myLooper() == Looper.getMainLooper()) { throw new RuntimeException("can not load bitmap from UI Thread"); } if (mDiskLruCache == null) { return null; } Bitmap bitmap = null; try { String key = Util.hashKeyFromUrl(url); DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); if (snapshot != null) { FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(CacheManager.DISK_CACHE_INDEX); FileDescriptor fileDescriptor = fileInputStream.getFD(); bitmap = imageResizer.decodeBitmapFromFileDescriptor(fileDescriptor, reqWidth, reqHeight); try { if (bitmap.getHeight() > 0) { if (bitmap != null) { lruCacheManager.addBitmapToMemoryCache(key, bitmap); } } } catch (Exception e) { return null; } } } catch (Exception e) { e.printStackTrace(); } return bitmap; } public long getUsableSpace(File path) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { return path.getUsableSpace(); } final StatFs statFs = new StatFs(path.getPath()); return statFs.getBlockSizeLong() * statFs.getAvailableBlocksLong(); } }
总结:整体逻辑的构建基本上都在CacheManager中,逻辑还是比较清楚的。
ps:完整代码在github,有需要的可以去下载。下载路径如下: