Fresco的缓存机制
来源:互联网 发布:软件数据保密协议 编辑:程序博客网 时间:2024/06/06 04:13
Fresco的图片获取及缓存由ImagePipeline模块实现,具体见下图:
图1 三级缓存
三级缓存
1.Bitmap缓存
Bitmap缓存存储Bitmap对象,这些Bitmap对象可以立刻用来显示或者用于后处理
在5.0以下系统,Bitmap缓存位于ashmem,这样Bitmap对象的创建和释放将不会引发GC,更少的GC会使你的APP运行得更加流畅。
5.0及其以上系统,相比之下,内存管理有了很大改进,所以Bitmap缓存直接位于Java的heap上。
当应用在后台运行时,该内存会被清空。
2.未解码图片的内存缓存
这个缓存存储的是原始压缩格式的图片。从该缓存取到的图片在使用之前,需要先进行解码。
如果有调整大小,旋转,或者WebP编码转换工作需要完成,这些工作会在解码之前进行。
APP在后台时,这个缓存同样会被清空。
3.文件缓存
和未解码的内存缓存相似,文件缓存存储的是未解码的原始压缩格式的图片,在使用之前同样需要经过解码等处理。
图片获取顺序
和内存缓存不一样,APP在后台时,内容是不会被清空的。即使关机也不会。用户可以随时用系统的设置菜单中进行清空缓存操作。
在Fresco介绍:Android的一个新图片库中,我们已经知道Fresco的缓存是由Producer/Consumer的框架来实现的。
图片获取是由各级Producer实现的,而将获取到的图片添加到缓存中是由各级Cusumer来实现的。
关于如何使用各级Producer获取图片的顺序见:\imagepipeline\src\main\java\com\facebook\imagepipeline\core\ProducerSequenceFactory.java
从如下函数可以看出整个的处理过程:
private Producer<CloseableReference<CloseableImage>> getBasicDecodedImageSequence( ImageRequest imageRequest) { Preconditions.checkNotNull(imageRequest); Uri uri = imageRequest.getSourceUri(); Preconditions.checkNotNull(uri, "Uri is null."); if (UriUtil.isNetworkUri(uri)) { return getNetworkFetchSequence(); } else if (UriUtil.isLocalFileUri(uri)) { if (MediaUtils.isVideo(MediaUtils.extractMime(uri.getPath()))) { return getLocalVideoFileFetchSequence(); } else { return getLocalImageFileFetchSequence(); } } else if (UriUtil.isLocalContentUri(uri)) { return getLocalContentUriFetchSequence(); } else if (UriUtil.isLocalAssetUri(uri)) { return getLocalAssetFetchSequence(); } else if (UriUtil.isLocalResourceUri(uri)) { return getLocalResourceFetchSequence(); } else if (UriUtil.isDataUri(uri)) { return getDataFetchSequence(); } else { String uriString = uri.toString(); if (uriString.length() > 30) { uriString = uriString.substring(0, 30) + "..."; } throw new RuntimeException("Unsupported uri scheme! Uri is: " + uriString); }}
我们先看getNetworkFetchSequence(),即从网络中加载图片需要经过怎样的处理:
/** * swallow result if prefetch -> bitmap cache get -> * background thread hand-off -> multiplex -> bitmap cache -> decode -> multiplex -> * encoded cache -> disk cache -> (webp transcode) -> network fetch. */ private synchronized Producer<CloseableReference<CloseableImage>> getNetworkFetchSequence() { if (mNetworkFetchSequence == null) { mNetworkFetchSequence = newBitmapCacheGetToDecodeSequence(getCommonNetworkFetchToEncodedMemorySequence()); } return mNetworkFetchSequence; }
该函数主要是返回newBitmapCacheGetToDecodeSequence()创建的Producer序列。分为两部分,一部分是从Bitmap缓存获取数据到未解码图片的内存缓存的Producer序列,另一部分是从网络获取数据到未解码图片的内存缓存的Producer序列。
从Bitmap缓存到未解码图片的内存缓存的Producer序列
具体实现如下:
/** * Same as {@code newBitmapCacheGetToBitmapCacheSequence} but with an extra DecodeProducer. * @param inputProducer producer providing the input to the decode * @return bitmap cache get to decode sequence */ private Producer<CloseableReference<CloseableImage>> newBitmapCacheGetToDecodeSequence( Producer<EncodedImage> inputProducer) { DecodeProducer decodeProducer = mProducerFactory.newDecodeProducer(inputProducer); return newBitmapCacheGetToBitmapCacheSequence(decodeProducer); } /** * Bitmap cache get -> thread hand off -> multiplex -> bitmap cache * @param inputProducer producer providing the input to the bitmap cache * @return bitmap cache get to bitmap cache sequence */ private Producer<CloseableReference<CloseableImage>> newBitmapCacheGetToBitmapCacheSequence( Producer<CloseableReference<CloseableImage>> inputProducer) { BitmapMemoryCacheProducer bitmapMemoryCacheProducer = mProducerFactory.newBitmapMemoryCacheProducer(inputProducer); BitmapMemoryCacheKeyMultiplexProducer bitmapKeyMultiplexProducer = mProducerFactory.newBitmapMemoryCacheKeyMultiplexProducer(bitmapMemoryCacheProducer); ThreadHandoffProducer<CloseableReference<CloseableImage>> threadHandoffProducer = mProducerFactory.newBackgroundThreadHandoffProducer( bitmapKeyMultiplexProducer, mThreadHandoffProducerQueue); return mProducerFactory.newBitmapMemoryCacheGetProducer(threadHandoffProducer); }
BitmapMemoryCacheGetProducer
对应图1中的”Memory Cache Read”继承了 BitmapMemoryCacheProducer类,只从Bitmap缓存中读取数据,只有该Producer是在UI线程中执行的。
public class BitmapMemoryCacheGetProducer extends BitmapMemoryCacheProducer { @VisibleForTesting static final String PRODUCER_NAME = "BitmapMemoryCacheGetProducer"; public BitmapMemoryCacheGetProducer( MemoryCache<CacheKey, CloseableImage> memoryCache, CacheKeyFactory cacheKeyFactory, Producer<CloseableReference<CloseableImage>> inputProducer) { super(memoryCache, cacheKeyFactory, inputProducer); } @Override protected Consumer<CloseableReference<CloseableImage>> wrapConsumer( final Consumer<CloseableReference<CloseableImage>> consumer, final CacheKey cacheKey) { // since this cache is read-only, we can pass our consumer directly to the next producer return consumer; }}
上述Producer流中的ThreadHandoffProducer之后的图片获取都在非UI线程中获取,即图1中绿色的部分。
BitmapMemoryCacheProducer
该类与BitmapMemoryCacheGetProducer的不同之处是,它在缓存中不存在数据时,会创建相应的Consumer,使用mMemoryCache.cache(cacheKey, newResult)将解压后的图片数据缓存到内存中去。
public class BitmapMemoryCacheProducer implements Producer<CloseableReference<CloseableImage>> {... @Override public void produceResults( final Consumer<CloseableReference<CloseableImage>> consumer, final ProducerContext producerContext) { ... final CacheKey cacheKey = mCacheKeyFactory.getBitmapCacheKey(imageRequest, callerContext); CloseableReference<CloseableImage> cachedReference = mMemoryCache.get(cacheKey); if (cachedReference != null) { boolean isFinal = cachedReference.get().getQualityInfo().isOfFullQuality(); if (isFinal) { listener.onProducerFinishWithSuccess( requestId, getProducerName(), listener.requiresExtraMap(requestId) ? ImmutableMap.of(VALUE_FOUND, "true") : null); consumer.onProgressUpdate(1f); } consumer.onNewResult(cachedReference, isFinal); cachedReference.close(); if (isFinal) { return; } } if (producerContext.getLowestPermittedRequestLevel().getValue() >= ImageRequest.RequestLevel.BITMAP_MEMORY_CACHE.getValue()) { listener.onProducerFinishWithSuccess( requestId, getProducerName(), listener.requiresExtraMap(requestId) ? ImmutableMap.of(VALUE_FOUND, "false") : null); consumer.onNewResult(null, true); return; } Consumer<CloseableReference<CloseableImage>> wrappedConsumer = wrapConsumer(consumer, cacheKey); listener.onProducerFinishWithSuccess( requestId, getProducerName(), listener.requiresExtraMap(requestId) ? ImmutableMap.of(VALUE_FOUND, "false") : null); mInputProducer.produceResults(wrappedConsumer, producerContext); } protected Consumer<CloseableReference<CloseableImage>> wrapConsumer( final Consumer<CloseableReference<CloseableImage>> consumer, final CacheKey cacheKey) { return new DelegatingConsumer< CloseableReference<CloseableImage>, CloseableReference<CloseableImage>>(consumer) { @Override public void onNewResultImpl(CloseableReference<CloseableImage> newResult, boolean isLast) { // ignore invalid intermediate results and forward the null result if last if (newResult == null) { if (isLast) { getConsumer().onNewResult(null, true); } return; } // stateful results cannot be cached and are just forwarded if (newResult.get().isStateful()) { getConsumer().onNewResult(newResult, isLast); return; } // if the intermediate result is not of a better quality than the cached result, // forward the already cached result and don't cache the new result. if (!isLast) { CloseableReference<CloseableImage> currentCachedResult = mMemoryCache.get(cacheKey); if (currentCachedResult != null) { try { QualityInfo newInfo = newResult.get().getQualityInfo(); QualityInfo cachedInfo = currentCachedResult.get().getQualityInfo(); if (cachedInfo.isOfFullQuality() || cachedInfo.getQuality() >= newInfo.getQuality()) { getConsumer().onNewResult(currentCachedResult, false); return; } } finally { CloseableReference.closeSafely(currentCachedResult); } } } // cache and forward the new result CloseableReference<CloseableImage> newCachedResult = mMemoryCache.cache(cacheKey, newResult); try { if (isLast) { getConsumer().onProgressUpdate(1f); } getConsumer().onNewResult( (newCachedResult != null) ? newCachedResult : newResult, isLast); } finally { CloseableReference.closeSafely(newCachedResult); } } }; }}
DecodeProducer
从未解码图片的内存缓存区获取数据并做解压处理,对应图1中的”Decode”。
至此,从Bitmap获取图片需要使用到的Producer的顺序及如何处理已经整理完毕。
从网络获取数据到未解码图片的内存缓存的Producer序列
/** * multiplex -> encoded cache -> disk cache -> (webp transcode) -> network fetch. */ private synchronized Producer<EncodedImage> getCommonNetworkFetchToEncodedMemorySequence() { if (mCommonNetworkFetchToEncodedMemorySequence == null) { Producer<EncodedImage> inputProducer = newEncodedCacheMultiplexToTranscodeSequence( mProducerFactory.newNetworkFetchProducer(mNetworkFetcher)); mCommonNetworkFetchToEncodedMemorySequence = ProducerFactory.newAddImageTransformMetaDataProducer(inputProducer); if (mResizeAndRotateEnabledForNetwork && !mDownsampleEnabled) { mCommonNetworkFetchToEncodedMemorySequence = mProducerFactory.newResizeAndRotateProducer( mCommonNetworkFetchToEncodedMemorySequence); } } return mCommonNetworkFetchToEncodedMemorySequence; }
ResizeAndRotateProducer
该类创建了TransformingConsumer 对象,对图片做大小和角度的转换(对应图1中的Transform)。
public class ResizeAndRotateProducer implements Producer<EncodedImage> {...@Override public void produceResults( final Consumer<EncodedImage> consumer, final ProducerContext context) { mInputProducer.produceResults(new TransformingConsumer(consumer, context), context); } private class TransformingConsumer extends DelegatingConsumer<EncodedImage, EncodedImage> {...@Override protected void onNewResultImpl(@Nullable EncodedImage newResult, boolean isLast) { if (mIsCancelled) { return; } if (newResult == null) { if (isLast) { getConsumer().onNewResult(null, true); } return; } TriState shouldTransform = shouldTransform(mProducerContext.getImageRequest(), newResult); // ignore the intermediate result if we don't know what to do with it if (!isLast && shouldTransform == TriState.UNSET) { return; } // just forward the result if we know that it shouldn't be transformed if (shouldTransform != TriState.YES) { getConsumer().onNewResult(newResult, isLast); return; } // we know that the result should be transformed, hence schedule it if (!mJobScheduler.updateJob(newResult, isLast)) { return; } if (isLast || mProducerContext.isIntermediateResultExpected()) { mJobScheduler.scheduleJob(); } } private void doTransform(EncodedImage encodedImage, boolean isLast) { mProducerContext.getListener().onProducerStart(mProducerContext.getId(), PRODUCER_NAME); ImageRequest imageRequest = mProducerContext.getImageRequest(); PooledByteBufferOutputStream outputStream = mPooledByteBufferFactory.newOutputStream(); Map<String, String> extraMap = null; EncodedImage ret = null; InputStream is = null; try { int numerator = getScaleNumerator(imageRequest, encodedImage); extraMap = getExtraMap(encodedImage, imageRequest, numerator); is = encodedImage.getInputStream(); JpegTranscoder.transcodeJpeg( is, outputStream, getRotationAngle(imageRequest, encodedImage), numerator, DEFAULT_JPEG_QUALITY); CloseableReference<PooledByteBuffer> ref = CloseableReference.of(outputStream.toByteBuffer()); try { ret = new EncodedImage(ref); ret.setImageFormat(ImageFormat.JPEG); try { ret.parseMetaData(); mProducerContext.getListener(). onProducerFinishWithSuccess(mProducerContext.getId(), PRODUCER_NAME, extraMap); getConsumer().onNewResult(ret, isLast); } finally { EncodedImage.closeSafely(ret); } } finally { CloseableReference.closeSafely(ref); } } catch (Exception e) { mProducerContext.getListener(). onProducerFinishWithFailure(mProducerContext.getId(), PRODUCER_NAME, e, extraMap); getConsumer().onFailure(e); return; } finally { Closeables.closeQuietly(is); outputStream.close(); } }...
AddImageTransformMetaDataProducer
添加图片的MetaData信息
EncodedMemoryCacheProducer
与BitmapMemoryCacheProducer类似,在缓存中不存在数据时,会创建相应的Consumer,使用 cachedResult = mMemoryCache.cache(cacheKey, ref);将图片数据缓存到未解码图片的内存缓存区中.对应代码如下:
/** * Memory cache producer for the encoded memory cache. */public class EncodedMemoryCacheProducer implements Producer<EncodedImage> { ... Consumer<EncodedImage> consumerOfInputProducer = new DelegatingConsumer< EncodedImage, EncodedImage>(consumer) { @Override public void onNewResultImpl(EncodedImage newResult, boolean isLast) { // intermediate or null results are not cached, so we just forward them if (!isLast || newResult == null) { getConsumer().onNewResult(newResult, isLast); return; } // cache and forward the last result CloseableReference<PooledByteBuffer> ref = newResult.getByteBufferRef(); if (ref != null) { CloseableReference<PooledByteBuffer> cachedResult; try { cachedResult = mMemoryCache.cache(cacheKey, ref); } finally { CloseableReference.closeSafely(ref); }...}
DiskCacheProducer
从disk缓存中获取数据,如果没有找到的话,使用NetworkFetchProducer获取数据并创建DiskCacheConsumer对象,将数据缓存到disk中。DiskCacheConsumer的代码如下:
private class DiskCacheConsumer extends DelegatingConsumer<EncodedImage, EncodedImage> { private final BufferedDiskCache mCache; private final CacheKey mCacheKey; private DiskCacheConsumer( final Consumer<EncodedImage> consumer, final BufferedDiskCache cache, final CacheKey cacheKey) { super(consumer); mCache = cache; mCacheKey = cacheKey; } @Override public void onNewResultImpl(EncodedImage newResult, boolean isLast) { if (newResult != null && isLast) { if (mChooseCacheByImageSize) { int size = newResult.getSize(); if (size > 0 && size < mForceSmallCacheThresholdBytes) { mSmallImageBufferedDiskCache.put(mCacheKey, newResult); } else { mDefaultBufferedDiskCache.put(mCacheKey, newResult); } } else { mCache.put(mCacheKey, newResult); } } getConsumer().onNewResult(newResult, isLast); }}
NetworkFetchProducer
从网络中获取图片数据,ImagePipeline 默认使用HttpURLConnection。应用可以根据自己需求使用不同的网络库。
public class NetworkFetchProducer implements Producer<EncodedImage> { @Override public void produceResults(Consumer<EncodedImage> consumer, ProducerContext context) { context.getListener() .onProducerStart(context.getId(), PRODUCER_NAME); final FetchState fetchState = mNetworkFetcher.createFetchState(consumer, context); mNetworkFetcher.fetch( fetchState, new NetworkFetcher.Callback() { @Override public void onResponse(InputStream response, int responseLength) throws IOException { NetworkFetchProducer.this.onResponse(fetchState, response, responseLength); } ... private void onResponse( FetchState fetchState, InputStream responseData, int responseContentLength) throws IOException { final PooledByteBufferOutputStream pooledOutputStream; if (responseContentLength > 0) { pooledOutputStream = mPooledByteBufferFactory.newOutputStream(responseContentLength); } else { pooledOutputStream = mPooledByteBufferFactory.newOutputStream(); } final byte[] ioArray = mByteArrayPool.get(READ_SIZE); try { int length; while ((length = responseData.read(ioArray)) >= 0) { if (length > 0) { pooledOutputStream.write(ioArray, 0, length); maybeHandleIntermediateResult(pooledOutputStream, fetchState); float progress = calculateProgress(pooledOutputStream.size(), responseContentLength); fetchState.getConsumer().onProgressUpdate(progress); } } mNetworkFetcher.onFetchCompletion(fetchState, pooledOutputStream.size()); handleFinalResult(pooledOutputStream, fetchState); } finally { mByteArrayPool.release(ioArray); pooledOutputStream.close(); } }
该类的构造函数的参数NetworkFetcher用来建立http链接,默认的HttpURLConnection的相应实现可以作为一个参考. 见:\imagepipeline\src\main\java\com\facebook\imagepipeline\producers\HttpUrlConnectionNetworkFetcher.java
注:关于网络请求
Fresco给出了OkHttp的实现。如果需要使用OkHttp, 使用下面的依赖配置
dependencies { // your project's other dependencies compile "com.facebook.fresco:fresco:0.9.0+" compile 'com.facebook.fresco:imagepipeline-okhttp:0.9.0+'}
配置Image pipeline这时也有一些不同,不再使用ImagePipelineConfig.newBuilder,而是使用OkHttpImagePipelineConfigFactory:
Context context;OkHttpClient okHttpClient; // build on your ownImagePipelineConfig config = OkHttpImagePipelineConfigFactory .newBuilder(context, okHttpClient) . // other setters . // setNetworkFetchProducer is already called for you .build();Fresco.initialize(context, config);
另外也可以通过继承NetworkFetchProducer来使用自定义的网络层,此时在配置Image pipeline时,把producer传递给Image pipeline。
ImagePipelineConfig config = ImagePipelineConfig.newBuilder() .setNetworkFetchProducer(myNetworkFetchProducer); . // other setters .build();Fresco.initialize(context, config);
总结
从上面图片获取及缓存的整个过程可以看到Producer/Consumer的框架的强大之处。以上是个人的一些简单总结,有什么不对的地方麻烦指正。
- Fresco的缓存机制
- 关于Fresco的三级缓存机制
- Glide与Fresco缓存机制
- fackbook的Fresco的Image Pipeline以及自身的缓存机制
- Fresco正传(7):如何手动清理Fresco的缓存。
- Fresco 缓存自定的 Bitmap
- Fresco库,自定义图片缓存的key
- Android 图片缓存 - Fresco
- Fresco清理缓存
- 图片缓存--Fresco(一)
- Fresco 获取本地缓存
- Android 图片缓存 - Fresco
- fresco清除缓存
- Fresco清理缓存
- Fresco判断是否缓存
- Fresco三级缓存
- Fresco获取缓存的大小为-1的解决方法
- 关于Fresco的缓存清理的那些事
- 分分钟解决iOS开发中App启动广告的功能
- java 原型模式
- abc
- Python 解析配置模块之ConfigParser详解
- 设计一个通用的BaseActivity
- Fresco的缓存机制
- abc
- 关于JavaScript中的delete
- python连接oracle数据库
- Java线程中run和start方法的区别
- 【Halcon】Halcon学习之七:改变图像的现实方式和大小
- idea 的10大常用快捷键
- Unity3d文件部署到IIS,图解 配置说明
- C++之overload、shadow和override的区别