对于魏则西 事件。。。。。
原文地址:http://www.fresco-cn.org/docs/configure-image-pipeline.html#_
Image Pipeline介绍编辑和纠错
Image pipeline 负责完成加载图像,变成Android设备可呈现的形式所要做的每个事情。
大致流程如下:
- 检查内存缓存,如有,返回
- 后台线程开始后续工作
- 检查是否在未解码内存缓存中。如有,解码,变换,返回,然后缓存到内存缓存中。
- 检查是否在文件缓存中,如果有,变换,返回。缓存到未解码缓存和内存缓存中。
- 从网络或者本地加载。加载完成后,解码,变换,返回。存到各个缓存中。
既然本身就是一个图片加载组件,那么一图胜千言。
上图中,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个线程池:
- 3个线程用于网络下载
- 两个线程用于磁盘操作: 本地文件的读取,磁盘缓存操作。
- 两个线程用于CPU相关的操作: 解码,转换,以及后处理等后台操作。
对于网络下载,你可以定制网络层的操作,具体参考:自定义网络层加载.
对于其他操作,如果要改变他们的行为,传入一个ExecutorSupplier即可。
内存缓存的配置@L_404_13@对象用于内存状态控制。
配置磁盘缓存 你可使用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。
ImagePipeline imagePipeline = FrescogetImagePipeline();
Uri uri;
imagePipelineevictFromMemoryCache(uri);
imagePipelineevictFromDiskCache);
// combines above two lines
imagePipelineevictFromCache 如同上面一样,evictFromDiskCache(Uri)
假定你使用的是默认的CacheKeyFactory。如果你自定义,请使用evictFromDiskCache(ImageRequest)
。
清除缓存
();
imagePipelineclearMemoryCachesclearDiskCaches();
clearCaches();
用一个文件还是两个文件缓存? 如果要使用2个缓存,在配置image pipeline时调用setMainDiskCacheConfig
和setSmallImageDiskCacheConfig
方法即可。
大部分的应用有一个文件缓存就够了,但是在一些情况下,你可能需要两个缓存。比如你也许想把小文件放在一个缓存中,大文件放在另外一个文件中,这样小文件就不会因大文件的频繁变动而被从缓存中移除。
至于什么是小文件,这个由应用来区分,在创建image request,设置ImageType即可:
ImageRequest request = ImageRequestnewBuilderWithSourceUrisetImageType(ImageTypeSMALL)
如果你仅仅需要一个缓存,那么不调用setSmallImageDiskCacheConfig
即可。Image pipeline 默认会使用同一个缓存,同时ImageType
也会被忽略。
内存用量的缩减 在配置Image pipeline时,我们可以指定每个缓存最大的内存用量。但是有时我们可能会想缩小内存用量。比如应用中有其他数据需要占用内存,不得不把图片缓存清除或者减小 或者我们想检查看看手机是否已经内存不够了。
Fresco的缓存实现了DiskTrimmable或者MemoryTrimmable接口。这两个接口负责从各自的缓存中移除内容。
在应用中,可以给Image pipeline配置上实现了DiskTrimmableRegistry和MemoryTrimmableRegistry接口的对象。
实现了这两个接口的对象保持着一个列表,列表中的各个元素在内存不够时,缩减各自的内存用量。
直接使用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:
();
DataSource<CloseableReference<CloseableImage>>
dataSource = imagePipelinefetchDecodedImage(imageRequest 关于如果接收数据,请参考数据源章节。
忽略解码 如果你不保持图片原始格式,不执行解码,使用fetchEncodedImage
即可:
DataSource<PooledByteBufferfetchEncodedImage);
从Bitmap缓存中立刻取到结果 不像其他缓存,如果图片在内存缓存中有的话,可以在UI线程立刻拿到结果。
>> dataSource =
mImagePipelinefetchImageFromBitmapCache);
CloseableReference> imageReference;
try {
imageReference = dataSourcegetResult();
if (imageReference != null) {
CloseableImage image = imageReferenceget();
// do something with the image
} finally {
dataSourceclose();
CloseableReferencecloseSafely(imageReference);
}
千万不要省略掉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. 持有者在离开作用域之前,需要关闭引用 创建了一个引用,但是没有传递给其他调用者,在结束时,需要关闭。
gee{
CloseableReference> ref = foo{
haa(ref);
{
ref();
finally
中最适合做此类事情了。
3. 除了引用的持有者,闲杂人等不得关闭引用 作为一个参数传递,调用者持有这个引用,在下面的函数体中,不能关闭引用。
haa(CloseableReference<?> ref{
Logprintln"Haa: " + ref());
如果调用了.close()
,调用者尝试调用.get()
时,会抛出IllegalStateException
4. 在赋值给变量前,先进行clone 在类中使用:
class MyClass > myValRef;
mmm> ref{
myValRef = refclone};
// caller can now safely close its copy as we made our own clone.
close{
CloseableReference(myValRef}
// MyClass的调用者需要关闭myValRef
在内部中使用:
原文链接:https://www.f2er.com/javaschema/283941.html
猜你在找的设计模式相关文章