什么网站做简历比较好,有什么网站可以叫人做图,做信息图的网站,服务称赞的项目管理平台背景#xff1a;
相册-图片后处理场景#xff0c;需要先展示一张原图#xff0c;同时后台对图片进行算法优化#xff0c;完成优化之后无缝替换原图展示#xff0c;同时保证后续都展示算法优化后的图片
图片加载采用 Glide 库实现
画重点#xff1a; 相册场景#xff…背景
相册-图片后处理场景需要先展示一张原图同时后台对图片进行算法优化完成优化之后无缝替换原图展示同时保证后续都展示算法优化后的图片
图片加载采用 Glide 库实现
画重点 相册场景意味着存在图片缓存的复用 无缝替换切换过程要很丝滑不能有闪烁等现象 后续展示优化后的图片说明后续缓存要复用算法优化后的不能用原图 因为是对图片进行优化所以原图和算法优化后的图片文件名一样
最开始的想法
步骤 先加载原图展示 等图片算法优化重新调用 glide 加载一遍
结果 重新加载之后还是原图效果
Glide 缓存机制
基于上述原因专门研究了下 Glide 的缓存机制重点是缓存的读取这一块
四级缓存机制
Glide 采用四级缓存机制主要分成两个模块内存缓存硬盘缓存
内存缓存防止应用重复将图片数据读到内存中硬盘缓存防止应用重复从网络或者其他地方重复下载和读取数据缓存Key既然有缓存那肯定需要对应的缓存Key通过按照一定规则生成缓存key之后根据key从缓存集中取出对应缓存复用
这里先简单讲下四级缓存机制然后再跟着源码过一遍
内存缓存-lru缓存
Glide 在内存中维持的一个缓存 map通过 lru 算法实现内存管理
private final LinkedHashMapT, Y cache new LinkedHashMapT, Y(100, 0.75f, true);
T缓存Key
Y缓存资源
内存缓存-弱引用缓存:
Glide 在内存中维持的一个弱引用缓存 map由于是弱引用这就意味着内存不足的时候先销毁这个
private final MapKey, WeakReferenceEngineResource? activeResources;
硬盘缓存-转换过缓存:
Glide 在硬盘上的缓存该缓存是经过各种变换后的缓存不是原始图片
硬盘缓存-原始图片缓存
Glide 在硬盘上的缓存该缓存是原始图片缓存
Glide 缓存加载流程
public T, Z, R LoadStatus load(Key signature, int width, int height, DataFetcherT fetcher,DataLoadProviderT, Z loadProvider, TransformationZ transformation, ResourceTranscoderZ, R transcoder,Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {Util.assertMainThread();long startTime LogTime.getLogTime();
final String id fetcher.getId();// 1. 生成 缓存key可以看出 key 的生成用到很多参数比如 id(图片路径)、signature、width、height还有 transformationEngineKey key keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),transcoder, loadProvider.getSourceEncoder());
// 2. 先从内存缓存-lru缓存中拿缓存如果拿到缓存调用 onResourceReadyEngineResource? cached loadFromCache(key, isMemoryCacheable);if (cached ! null) {cb.onResourceReady(cached);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey(Loaded resource from cache, startTime, key);}return null;}// 3. 内存缓存-lru中没有缓存则从弱引用缓存中拿如果拿到调用 onResourceReadyEngineResource? active loadFromActiveResources(key, isMemoryCacheable);if (active ! null) {cb.onResourceReady(active);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey(Loaded resource from active resources, startTime, key);}return null;}// 4. 内存缓存-弱引用中也没有缓存从硬盘缓存中拿或加载文件地址不管是拿硬盘缓存还是加载文件涉及到IO操作需要子线程处理EngineJob current jobs.get(key);if (current ! null) {current.addCallback(cb);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey(Added to existing load, startTime, key);}return new LoadStatus(cb, current);}
EngineJob engineJob engineJobFactory.build(key, isMemoryCacheable);DecodeJobT, Z, R decodeJob new DecodeJobT, Z, R(key, width, height, fetcher, loadProvider, transformation,transcoder, diskCacheProvider, diskCacheStrategy, priority);EngineRunnable runnable new EngineRunnable(engineJob, decodeJob, priority);jobs.put(key, engineJob);engineJob.addCallback(cb);engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey(Started new load, startTime, key);}return new LoadStatus(cb, engineJob);
}
内存缓存-lru缓存中拿缓存
private EngineResource? loadFromCache(Key key, boolean isMemoryCacheable) {if (!isMemoryCacheable) {return null;}// 如果从 lru 中能拿到缓存将该缓存从 lru 缓存中移除并存入 弱引用缓存EngineResource? cached getEngineResourceFromCache(key);if (cached ! null) {cached.acquire();activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));}return cached;
}
从硬盘缓存中拿或加载文件地址
private Resource? decode() throws Exception {// 这里有个策略判断只有支持硬盘缓存策略才能从硬盘中拿缓存if (isDecodingFromCache()) {// 从硬盘缓存中拿return decodeFromCache();} else {// 加载文件return decodeFromSource();}
}
// 从硬盘缓存中拿
private Resource? decodeFromCache() throws Exception {Resource? result null;try {// 5. 从硬盘缓存-转换过缓存中拿result decodeJob.decodeResultFromCache();} catch (Exception e) {if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, Exception decoding result from cache: e);}}
if (result null) {// 6. 硬盘缓存-转换过缓存中拿不到从原始图片缓存中拿result decodeJob.decodeSourceFromCache();}return result;
}
// 从硬盘缓存-转换过缓存中拿
public ResourceZ decodeResultFromCache() throws Exception {if (!diskCacheStrategy.cacheResult()) {return null;}
long startTime LogTime.getLogTime();ResourceT transformed loadFromCache(resultKey);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey(Decoded transformed from cache, startTime);}startTime LogTime.getLogTime();ResourceZ result transcode(transformed);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey(Transcoded transformed from cache, startTime);}return result;
}
// 从硬盘缓存-原始图片缓存中拿
public ResourceZ decodeSourceFromCache() throws Exception {if (!diskCacheStrategy.cacheSource()) {return null;}
long startTime LogTime.getLogTime();// 这里值得注意的是这里用的key和其他三个缓存中用到的不一样这里用的是 resultKey.getOriginalKey() 就是原始图片地址不过也能理解加载原始图片缓存也不需要额外的key信息只需要文件地址就行了ResourceT decoded loadFromCache(resultKey.getOriginalKey());if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey(Decoded source from cache, startTime);}return transformEncodeAndTranscode(decoded);
}
理完Glide的加载机制之后就很好理解为什么重新加载之后还是原图所以进行了优化
优化方案二
加载原图时
glideOptions new RequestOptions().signature(new ObjectKey(signatureKey));
加载优化后图片时
glideOptions new RequestOptions();
加载原图时多调用了 signature 方法这样加载优化后图片时由于和加载原图时的缓存key不一样会重新进行加载
优化方案二解决了优化图片不加载问题但是由于重新加载图片会导致切换过程出现很明显的闪烁这里可以通过 placeholder 方法优化
优化方案三
// 1. 获取当前 imageView 的 drawable
Drawable drawable imageView.getDrawable();
Bitmap placeholderBitmap null;
// 2. drawable 转成 bitmap
if(drawable instanceof BitmapDrawable){placeholderBitmap ((BitmapDrawable) drawable).getBitmap();
}
// 3. 由于 imageView 的 drawable 可能会被回收所以需要将 bitmap 重新 clone 一份
Bitmap copiedBitmap null;
if(placeholderBitmap ! null){copiedBitmap placeholderBitmap.copy(placeholderBitmap.getConfig(), true);
}
// 4. 将 bitmap 作为 占位符传入
if(copiedBitmap ! null !copiedBitmap.isRecycled()){glideOptions new RequestOptions().frame(0).placeholder(new BitmapDrawable(context.getResources(), copiedBitmap)).override(size.getWidth(), size.getHeight());
}else {glideOptions new RequestOptions().frame(0).override(size.getWidth(), size.getHeight());
}
至此效果实现