Glide 缓存原理实现
Glide 缓存原理实现
专注于Android开发,分享经验总结,欢迎加入
QQ群:686809487
Glide使用方式如下:
Glide.with(MainActivity.this)
.load(path)
.into(iv);
在这里插入图片描述Glide缓存分为:活动缓存、内存缓存、Bitmap复用池、磁盘缓存、加载外置(网络或者SD卡)、绑定生命周期
具体的流程如下
-
资源封装
Key -- 对Value的唯一性进行描述
Value -- 对Bitmap的封装
-
活动缓存
// 用容器存储
private HashMap<String, WeakReference<Value>> map = new HashMap<>();
// 继承WeakReference(目的:为了监听这个弱引用 是否被回收了)
public class CustomWeakReference extends WeakReference {
public String key;
public CustomWeakReference(Object referent, ReferenceQueue queue, String key) {
super(referent, queue);
this.key = key;
}
}
/**
* 目的:为了监听这个弱引用 是否被回收了
* queue.remove() 会阻塞线程
*
* @return
*/
public ReferenceQueue<Value> getQueue() {
if (queue == null) {
queue = new ReferenceQueue<>();
// 监听这个弱引用 是否被回收了
thread = new Thread() {
@Override
public void run() {
while (!isCloseThread) {
if (!isShoudonRemove) {
try {
// 阻塞式的方法,被动调用queue.remove(),进行回收
Reference<? extends Value> remove = queue.remove();
CustomWeakReference customWeakReference = (CustomWeakReference) remove;
if (map != null && !map.isEmpty()) {
// 移除容器 !isShoudonRemove:为了区分手动移除 和 被动移除
map.remove(customWeakReference.key);
}
Log.d(TAG, "getQueue remove ");
} catch (InterruptedException e) {
Log.d(TAG, "getQueue InterruptedException e :" + e.getMessage());
e.printStackTrace();
}
}
}
}
};
thread.start();
}
return queue;
}
> 当活动缓存的值不在被使用时,从活动缓存移除,并加入都内存缓存
-
内存缓存
内存缓存 使用 Lru算法,继承自 LruCache<String, Value>
重写sizeOf(),entryRemoved()方法监听元素被移除
@Override protected int sizeOf(@NonNull String key, @NonNull Value value) { Bitmap bitmap = value.getBitmap(); // 最开始的时候 // int result = bitmap.getRowBytes() * bitmap.getHeight(); // API 12 3.0 // result = bitmap.getByteCount(); // 在bitmap内存复用上有区别 (所属的) // API 19 4.4 // result = bitmap.getAllocationByteCount(); // 在bitmap内存复用上有区别 (整个的) return Tool.getBitmapByteSize(bitmap); } /** * 被移除时监听 * 1.重复的key * 2.最少使用的元素会被移除 * * @param evicted * @param key * @param oldValue * @param newValue */ @Override protected void entryRemoved(boolean evicted, String key, Value oldValue, Value newValue) { super.entryRemoved(evicted, key, oldValue, newValue); // !shoudonRemove == 被动的 if (memoryCacheCallback != null && !shoudonRemove) { memoryCacheCallback.entryRemovedMemoryCache(key, oldValue); } }
当内存缓存移除最少使用值的时候,加入到BitmapPool复用池(复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片)
-
Bitmap复用池
LruBitmapPool extends LruCache<Integer, Bitmap>
使用容器TreeMap<Integer, Bitmap> treeMap = new TreeMap<>();是为了筛选
重写put()和get()方法,加入存取的一些条件
@Override public void put(Bitmap bitmap) { Tool.checkNotEmpty(bitmap); // TODO 复用的条件1 bitmap.isMutable() if (!bitmap.isMutable()) { if (!bitmap.isRecycled()) { bitmap.recycle(); Log.d(TAG, "put: 复用的条件1 Bitmap.ismutable 是false,条件不满足,不能复用 添加..." + bitmap); return; } } // TODO 复用的条件2 如果添加复用的Bitmap大小,大于Lru MaxSize 就不复用 int bitmapByteSize = Tool.getBitmapByteSize(bitmap); if (bitmapByteSize > maxSize()) { if (!bitmap.isRecycled()) { bitmap.recycle(); } Log.d(TAG, "put: 复用的条件2 Bitmap.Size大于LruMaxSize,条件不满足,不能复用 添加..."); return; } // 添加到 Lru Cahce中去 put(bitmapByteSize, bitmap); // 保存到 TreeMap 是为了筛选 treeMap.put(bitmapByteSize, null); Log.d(TAG, "put: 添加到复用池了...."); } @Override public Bitmap get(int width, int height, Bitmap.Config config) { if (treeMap.isEmpty()) { Log.d(TAG, "treeMap:为空"); return null; } // config为null 默认给 Bitmap.Config.ARGB_8888 int bitmapByteSize = Tool.getBitmapByteSize(width, height, config); // TODO ceilingKey 获得 getSize这么大的key,同时还可以获得 比 getSize还要大的key Integer key = treeMap.ceilingKey(bitmapByteSize);// 获得 getSize这么大的key,同时还可以获得 比 getSize还要大的key if (key == null) { Log.d(TAG, "treeMap:找不到 保存的key"); return null; // 如果找不到 保存的key,就直接返回null,无法复用 } // 找出来的key 小于等于 (getSize * 2) if (key <= (bitmapByteSize * 2)) { Bitmap resultBitmap = remove(key); Log.d(TAG, "get: 从复用池获取:" + resultBitmap); return resultBitmap; } return null; }
BitmapFactory.Options options = new BitmapFactory.Options();
options.inBitmap = bitmapPoolResult;// bitmapPoolResult为null,不复用地址,复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片
-
磁盘缓存
使用JakeWharton开源项目DiskLruCache
https://github.com/JakeWharton/DiskLruCache
// TODO put 存入Value public void put(String key, Value value) { Tool.checkNotEmpty(key); OutputStream outputStream = null; DiskLruCache.Editor edit = null; try { edit = diskLruCache.edit(key); // index 不能大于 VALUE_COUNT outputStream = edit.newOutputStream(0); // 把bitmap写入到outputStream Bitmap bitmap = value.getBitmap(); if (bitmap != null) { bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); outputStream.flush(); } } catch (IOException e) { e.printStackTrace(); try { // Aborts this edit. This releases the edit lock so another edit may be // 中止此编辑。 这会释放编辑锁,因此可能需要进行其他编辑 if (edit != null) { edit.abort(); } } catch (IOException ex) { ex.printStackTrace(); Log.e(TAG, "put: editor.abort(); e:" + ex.getMessage()); } } finally { try { if (edit != null) { edit.commit(); } diskLruCache.flush(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "put: editor.commit(); e:" + e.getMessage()); } if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "put: outputStream.close(); e:" + e.getMessage()); } } } } // TODO get 获取Value public Value get(String key, BitmapPool bitmapPool) { InputStream inputStream = null; try { DiskLruCache.Snapshot snapshot = diskLruCache.get(key); // 判断快照不为null的情况下,在去读取操作 if (snapshot != null) { // index 不能大于 VALUE_COUNT inputStream = snapshot.getInputStream(0); // 复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片 Bitmap bitmap = Tool.getIOBitmap(inputStream, bitmapPool, true); Value value = Value.getInstance(); value.setKey(key); value.setBitmap(bitmap); return value; } } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "get: IOException; e:" + e.getMessage()); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "get: inputStream.close(); e:" + e.getMessage()); } } } return null; }
-
生命周期
.with(Context) 与当前的上下文绑定;通过上下文绑定一个Fragment实现管理生命周期
Context分为:
-
Application无法绑定生命周期
-
FragmentActivity 可绑定生命周期
-
Activity 可绑定生命周期
public interface LifecycleCallback { // 生命周期初始化了 public void glideInitAction(); // 生命周期 停止了 public void glideStopAction(); // 生命周期 释放 操作了 public void glideRecycleAction(); }
-
-
网络加载/SD卡加载
网络加载:开一个线程池去加载,通过HttpConnection
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()); threadPoolExecutor.execute(this); 开子线程去加载 @Override public void run() { InputStream inputStream = null; HttpURLConnection httpURLConnection = null; try { URL url = new URL(path); URLConnection urlConnection = url.openConnection(); httpURLConnection = (HttpURLConnection) urlConnection; httpURLConnection.setReadTimeout(5000); final int responseCode = httpURLConnection.getResponseCode(); if (HttpURLConnection.HTTP_OK == responseCode) { inputStream = httpURLConnection.getInputStream(); // 复用Bitmap地址,避免频繁调用内存空间,防止内存抖动、内存碎片 final Bitmap bitmap = Tool.getIOBitmap(inputStream, null, false); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { Value value = Value.getInstance(); value.setBitmap(bitmap); responseListener.responseSuccess(value); } }); } else { // 失败 切换主线程 new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { responseListener.responseException(new IllegalStateException("请求失败 请求码:" + responseCode)); } }); } } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); e.printStackTrace(); Log.d(TAG, "run: 关闭 inputStream.close(); e:" + e.getMessage()); } } if (httpURLConnection != null) { httpURLConnection.disconnect(); } } }
SD卡加载:
Bitmap bitmap = BitmapFactory.decodeFile(path); Value value = Value.getInstance(); value.setBitmap(bitmap); // 回调成功 Log.d(TAG, " LoadDataManager 从本地获取到bitmap"); responseListener.responseSuccess(value);
-
日志记录
源码查看,关注公众号回复 Glide ,获取
微信公众号 -->> 他晓 (欢迎加入)