Fresco的图片获取及缓存由ImagePipeline模块实现,具体见下图:
三级缓存
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> 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> 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> newBitmapCacheGetToDecodeSequence(
Producer 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> newBitmapCacheGetToBitmapCacheSequence(
Producer> inputProducer) {
BitmapMemoryCacheProducer bitmapMemoryCacheProducer =
mProducerFactory.newBitmapMemoryCacheProducer(inputProducer);
BitmapMemoryCacheKeyMultiplexProducer bitmapKeyMultiplexProducer =
mProducerFactory.newBitmapMemoryCacheKeyMultiplexProducer(bitmapMemoryCacheProducer);
ThreadHandoffProducer> 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 memoryCache,
CacheKeyFactory cacheKeyFactory,
Producer> inputProducer) {
super(memoryCache, cacheKeyFactory, inputProducer);
}
@Override
protected Consumer> wrapConsumer(
final Consumer> 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> {
...
@Override
public void produceResults(
final Consumer> consumer,
final ProducerContext producerContext) {
...
final CacheKey cacheKey = mCacheKeyFactory.getBitmapCacheKey(imageRequest, callerContext);
CloseableReference 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> wrappedConsumer = wrapConsumer(consumer, cacheKey);
listener.onProducerFinishWithSuccess(
requestId,
getProducerName(),
listener.requiresExtraMap(requestId) ? ImmutableMap.of(VALUE_FOUND, "false") : null);
mInputProducer.produceResults(wrappedConsumer, producerContext);
}
protected Consumer> wrapConsumer(
final Consumer> consumer,
final CacheKey cacheKey) {
return new DelegatingConsumer<
CloseableReference,
CloseableReference>(consumer) {
@Override
public void onNewResultImpl(CloseableReference 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 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 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 getCommonNetworkFetchToEncodedMemorySequence() {
if (mCommonNetworkFetchToEncodedMemorySequence == null) {
Producer 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 {
...
@Override
public void produceResults(
final Consumer consumer,
final ProducerContext context) {
mInputProducer.produceResults(new TransformingConsumer(consumer, context), context);
}
private class TransformingConsumer extends DelegatingConsumer {
...
@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 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 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 {
...
Consumer 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 ref = newResult.getByteBufferRef();
if (ref != null) {
CloseableReference cachedResult;
try {
cachedResult = mMemoryCache.cache(cacheKey, ref);
} finally {
CloseableReference.closeSafely(ref);
}
...
}
DiskCacheProducer
从disk缓存中获取数据,如果没有找到的话,使用NetworkFetchProducer获取数据并创建DiskCacheConsumer对象,将数据缓存到disk中。DiskCacheConsumer的代码如下:
private class DiskCacheConsumer extends DelegatingConsumer {
private final BufferedDiskCache mCache;
private final CacheKey mCacheKey;
private DiskCacheConsumer(
final Consumer 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 {
@Override
public void produceResults(Consumer 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 own
ImagePipelineConfig 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的框架的强大之处。以上是个人的一些简单总结,有什么不对的地方麻烦指正。