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的框架的强大之处。以上是个人的一些简单总结,有什么不对的地方麻烦指正。

0 1