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<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 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的框架的強大之處。以上是個人的一些簡單總結,有什么不對的地方麻煩指正。