Fresco的缓存机制

Fresco的图片获取及缓存由ImagePipeline模块实现,具体见下图:


Fresco的缓存机制_第1张图片
7T.png

三级缓存

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

你可能感兴趣的:(Fresco的缓存机制)