Image PipeLine指南

前端之家收集整理的这篇文章主要介绍了Image PipeLine指南前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

对于魏则西 事件。。。。。

原文地址:http://www.fresco-cn.org/docs/configure-image-pipeline.html#_

Image Pipeline介绍编辑和纠错

Image pipeline 负责完成加载图像,变成Android设备可呈现的形式所要做的每个事情。

大致流程如下:

  1. 检查内存缓存,如有,返回
  2. 后台线程开始后续工作
  3. 检查是否在未解码内存缓存中。如有,解码,变换,返回,然后缓存到内存缓存中。
  4. 检查是否在文件缓存中,如果有,变换,返回。缓存到未解码缓存和内存缓存中。
  5. 从网络或者本地加载。加载完成后,解码,变换,返回。存到各个缓存中。

既然本身就是一个图片加载组件,那么一图胜千言。

上图中,disk cache实际包含了未解码的内存缓存在内,统一在一起只是为了逻辑稍微清楚一些。关于缓存,更多细节可以参考这里

Image pipeline 可以从本地文件加载文件,也可以从网络。支持PNG,GIF,WebP,JPEG。

各个Android系统的WebP适配

在3.0系统之前,Android是不支持WebP格式的。在4.1.2之前,扩展WebP格式是不支持的。 在Image pipeline的支持下,从2.3之后,都可以使用WebP格式。

配置Image Pipeline编辑和纠错

对于大多数的应用,Fresco的初始化,只需要以下一句代码:

Fresco.initialize(context);

对于那些需要更多进一步配置的应用,我们提供了ImagePipelineConfig

以下是一个示例配置,列出了所有可配置的选项。几乎没有应用是需要以下这所有的配置的,列出来仅仅是为了作为参考。

ImagePipelineConfig config = ImagePipelineConfignewBuilder()
    setBitmapMemoryCacheParamsSupplier(bitmapCacheParamsSupplier)
    setCacheKeyFactory(cacheKeyFactorysetEncodedMemoryCacheParamsSupplier(encodedCacheParamsSuppliersetExecutorSupplier(executorSuppliersetImageCacheStatsTracker(imageCacheStatsTrackersetMainDiskCacheConfig(mainDiskCacheConfigsetMemoryTrimmableRegistry(memoryTrimmableRegistry) 
    setNetworkFetchProducer(networkFetchProducersetPoolFactory(poolFactorysetProgressiveJpegConfig(progressiveJpegConfigsetRequestListeners(requestListenerssetSmallImageDiskCacheConfig(smallImageDiskCacheConfigbuild();
Fresco, config 请记得将配置好的ImagePipelineConfig传递给Fresco.initialize!否则仍旧是默认配置。

关于Supplier 许多配置的Builder都接受一个Supplier类型的参数而不是一个配置的实例。

创建时也许有一些麻烦,但这带来更多的利好:这允许在运行时改变创建行为。以内存缓存为例,每隔5分钟就可检查一下Supplier,根据实际情况返回不同类型。

如果你需要动态改变参数,那就是用Supplier每次都返回同一个对象。

Supplier<X> xSupplier = new Supplier>() {
  public X get() {
    return new X(xparam1xparam2...);
  }
);
// when creating image pipeline
setXSupplier(xSupplier);

线程池 Image pipeline 默认有3个线程池:

  1. 3个线程用于网络下载
  2. 两个线程用于磁盘操作: 本地文件的读取,磁盘缓存操作。
  3. 两个线程用于CPU相关的操作: 解码,转换,以及后处理等后台操作。

对于网络下载,你可以定制网络层的操作,具体参考:自定义网络层加载.

对于其他操作,如果要改变他们的行为,传入一个ExecutorSupplier即可。

内存缓存的配置 内存缓存和未解码的内存缓存的配置由一个Supplier控制,这个Supplier返回一个MemoryCacheParams对象用于内存状态控制。

配置磁盘缓存 你可使用Builder模式创建一个DiskCacheConfig:

DiskCacheConfig diskCacheConfig = DiskCacheConfig()
   set....
   ()

// when building ImagePipelineConfig
(diskCacheConfig)

缓存统计 如果你想统计缓存的命中率,你可以实现ImageCacheStatsTracker,在这个类中,每个缓存时间都有回调通知,基于这些事件,可以实现缓存的计数和统计

缓存

三级缓存

1. Bitmap缓存

Bitmap缓存存储Bitmap对象,这些Bitmap对象可以立刻用来显示或者用于后处理

在5.0以下系统,Bitmap缓存位于ashmem,这样Bitmap对象的创建和释放将不会引发GC,更少的GC会使你的APP运行得更加流畅。

5.0及其以上系统,相比之下,内存管理有了很大改进,所以Bitmap缓存直接位于Java的heap上。

当应用在后台运行时,该内存会被清空。

2. 未解码图片的内存缓存 这个缓存存储的是原始压缩格式的图片。从该缓存取到的图片在使用之前,需要先进行解码。

如果有调整大小,旋转,或者WebP编码转换工作需要完成,这些工作会在解码之前进行。

APP在后台时,这个缓存同样会被清空。

3. 文件缓存 和未解码的内存缓存相似,文件缓存存储的是未解码的原始压缩格式的图片,在使用之前同样需要经过解码等处理。

和内存缓存不一样,APP在后台时,内容是不会被清空的。即使关机也不会。用户可以随时用系统的设置菜单中进行清空缓存操作。

查找一个bitmap是否被缓存? 你可以使用ImagePipeline(http://frescolib.org/javadoc/reference/com/facebook/imagepipeline/core/ImagePipeline.html)检查bitmap是否在缓存中。

查询bitmap是否在内存缓存中的操作是同步的。java ImagePipeline imagePipeline = Fresco.getImagePipeline(); Uri uri; boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(uri);

查询bitmap是否在文件缓存中的操作时异步的。因为这个操作可以使用另一个线程操作。你可以这样使用。java DataSource<Boolean> inDiskCacheSource = imagePipeline.isInDiskCache(uri); DataSubscriber<Boolean> subscriber = new BaseDataSubscriber<Boolean>() { @Override protected void onNewResultImpl(DataSource<Boolean> dataSource) { if (!dataSource.isFinished()) { return; } boolean isInCache = dataSource.getResult(); // your code here } }; inDiskCacheSource.subscribe(subscriber,executor);以上API假设你使用默认的CacheKeyFactory。如果你自定义,你可能需要用把ImageRequest作为参数的方程,即imagePipeline.isInDiskCache(ImageRequest)imagePipeline.isInBitmapMemoryCache(ImageRequest)

清除缓存中的一条url ImagePipeline现有函数可以删除缓存中的一条url。

用一个文件还是两个文件缓存? 如果要使用2个缓存,在配置image pipeline调用setMainDiskCacheConfigsetSmallImageDiskCacheConfig方法即可。

大部分的应用有一个文件缓存就够了,但是在一些情况下,你可能需要两个缓存。比如你也许想把小文件放在一个缓存中,大文件放在另外一个文件中,这样小文件就不会因大文件的频繁变动而被从缓存中移除。

至于什么是小文件,这个由应用来区分,在创建image request,设置ImageType即可:

ImageRequest request = ImageRequestnewBuilderWithSourceUrisetImageType(ImageTypeSMALL)

如果你仅仅需要一个缓存,那么不调用setSmallImageDiskCacheConfig即可。Image pipeline 默认会使用同一个缓存,同时ImageType也会被忽略。

内存用量的缩减配置Image pipeline时,我们可以指定每个缓存最大的内存用量。但是有时我们可能会想缩小内存用量。比如应用中有其他数据需要占用内存,不得不把图片缓存清除或者减小 或者我们想检查看看手机是否已经内存不够了。

Fresco的缓存实现了DiskTrimmable或者MemoryTrimmable接口。这两个接口负责从各自的缓存中移除内容

在应用中,可以给Image pipeline配置上实现了DiskTrimmableRegistryMemoryTrimmableRegistry接口的对象。

实现了这两个接口的对象保持着一个列表,列表中的各个元素在内存不够时,缩减各自的内存用量。

直接使用Image Pipeline 本页介绍Image pipeline的高级用法,大部分的应用使用Drawees和image pipeline打交道就好了。

直接使用Image pipeline是较为有挑战的事情,这意味着要维护图片的内存使用。Drawees 会根据各种情况确定图片是否需要在内存缓存中,在需要时加载,在不需要时移除。直接使用的话,你需要自己完成这些逻辑。

Image pipeline返回的是一个CloseableReference对象。在这些对象不需要时,Drawees会调用.close()方法。如果你的应用不使用Drawees,那你需要自己完成这个事情。

Java的GC机制会在Bitmap不使用时,清理掉Bitmap。但要GC时总是太迟了,另外GC是很昂贵的开销。GC大对象也会带来性能问题,尤其是在5.0以下系统。

调用 pipeline 首先创建一个image request. 然后传递给ImagePipeline:

千万不要省略掉finally中的代码!

预加载图片 预加载图片可减少用户等待的时间,如果预加载的图片用户没有真正呈现给用户,那么就浪费了用户的流量,电量,内存等资源了。大多数应用,并不需要预加载。

Image pipeline 提供两种预加载方式。

预加载到文件缓存:

imagePipelineprefetchToDiskCache 预加载到内存缓存:

prefetchToBitmapCache);

数据源和数据订阅者 数据源Future,有些相似,都是异步计算的结果。

不同点在于,数据源对于一个调用会返回一系列结果,Future只返回一个。

提交一个Image request之后,Image pipeline返回一个数据源。从中获取数据需要使用数据订阅者(DataSubscriber).

当你仅仅需要Bitmap 如果你请求Image pipeline仅仅是为了获取一个Bitmap,对象。你可以利用简单易用的BaseBitmapDataSubscriber:

dataSourcesubscribe(BaseBitmapDataSubscriber{
    @Override
    public void onNewResultImpl(@Nullable Bitmap bitmap{
       // You can use the bitmap in only limited ways
      // No need to do any cleanup.
    }

    onFailureImpl(DataSource dataSource{
      // No cleanup required here.
    }
  });

看起来很简单,对吧。下面是一些小警告:

千万不要把bitmap复制给onNewResultImpl函数范围之外的任何变量。订阅者执行完操作之后,image pipeline 会回收这个bitmap,释放内存。在这个函数范围内再次使用这个Bitmap对象进行绘制将会导致IllegalStateException

通用的解决方 如果你就是想维持对这个Bitmap对象的引用,你不能维持纯Bitmap对象的引用,可以利用可关闭的引用(closeable references)BaseDataSubscriber:

DataSubscriber dataSubscriber =
    new BaseDataSubscriber>>() {
  @Override
  (
      DataSource>> dataSource{

    (!dataSourceisFinished()) {
      FLogv("Not yet finished - this is just another progressive scan.");
    }  

    CloseableReference> imageReference ();
    {
      {
        CloseableImage image ();
        // do something with the image
      {
        imageReference();
      }
    }
  {
    Throwable throwable getFailureCause// handle failure
  };

dataSource(dataSubscriberexecutor 这样,只要遵守可关闭的引用使用规则,你就可以把这个CloseableReference复制给其他变量了。

关闭的引用 本页内容仅为高级使用作参考

大部分的应用,直接使用Drawees就好了,不用考虑关闭的事情了。

Java带有垃圾收集功能,许多开发者习惯于不自觉地创建一大堆乱七八糟的对象,并且想当然地认为他们会从内存中想当然地消失。

在5.0系统之前,这样的做法对于操作Bitmap是极其糟糕的。Bitmap占用了大量的内存,大量的内存申请和释放引发频繁的GC,使得界面卡顿不已。

Bitmap 是Java中为数不多的能让Java开发者想念或者羡慕C++以及C++众多的指针库,比如Boost的东西。

Fresco的解决方案是:可关闭的引用(CloseableReference)

为了正确地使用它,请按以下步骤进行操作:

1. 调用者拥有这个引用 我们创建一个引用,但我们传递给了一个调用者,调用者将持有这个引用。

CloseableReference<Val> foo{
  Val val;
  return CloseableReferenceof(val}

2. 持有者在离开作用域之前,需要关闭引用 创建了一个引用,但是没有传递给其他调用者,在结束时,需要关闭。

猜你在找的设计模式相关文章