fresco(1):整体框架

之前有大概的了解fresco,也看过部分源代码,现在项目中使用的越来越多,觉得有必要整理一下。这篇博客主要是分析了fresco整体的框架。没有看过准备去看fresco源代码的童鞋可以看看这篇博客进行参考一下。基于fresco 1.5.0

怎么使用

fresco总体配置
1.Application中对fresco初始化。主要是配置所有的Drawee的公共样式。请求和配置(磁盘缓存、内存缓存等的大小路径)。下面一个简单的例子。

1
2
Fresco.initialize(this, ImagePipelineConfig.newBuilder(this).setXX().build(),
DraweeConfig.newBuilder().setXX().build());

freco使用配置
2.使用SimpleDraweeView。主要是配置图片的路径,展示的样式(圆角、默认图、失败图、重试图等等)。下面是一个简单例子。

1
2
3
4
5
6
7
8
GenericDraweeHierarchy hierarchy = getHierarchy();
hierarchy.setFadeDuration(fadeInMillis);
hierarchy.setFailureImage(failImage);
hierarchy.setPlaceholderImage(loadingImage);
DraweeController controller = Fresco.newDraweeControllerBuilder().setImageRequest(
ImageRequestBuilder.newBuilderWithSource(Uri.parse(url)).build()).setAutoPlayAnimations(true).setOldController(
getController()).build();
setController(controller); // 最终会去请求图片

整体的module 层次

fresco的module 层次
Drawee:控制展示,比如View加载到屏幕以后才去请求图片。控制默认图、错误图等的展示。

ImagePiple:处理不同来源图片的加载(网络、sd 卡等等)。

逻辑实现的框架:Producer、Consumer

fresco实现了很多的逻辑:从网络获取图片、从磁盘获取图片、缓存在磁盘、解码(解析gif)等等。如何将这些操作解耦是一个值得好好考虑的问题。这么多不同需求的组合,最开始想到是使用责任链模式,将每一小块单独出来,处理完以后给另外的一个小块。这样就相当于对每一个请求只是需要构造一个合适的”链子”就好了。

看一个网络请求完整的”链子”:

NetworkFetchProducer<- // 网络获取图片

WebpTranscodeProducer<- // webp 转化

DiskCacheWriteProducer(PartialDiskCacheProducer)<- // Partial逻辑(缓存已经下载的部分)

MediaVariationsFallbackProducer<- // 当不存在时,则会在相同的文件中选择最合适的展示

DiskCacheReadProducer<- // 读取磁盘缓存的内容。smallImageBufferedDiskCache 是为了快速命中吧

EncodedMemoryCacheProducer<- // 获取到了数据但是没有展示出来(没有解码)的情况

EncodedCacheKeyMultiplexProducer<-

AddImageTransformMetaDataProducer<- // 获取到相应的head meta

ResizeAndRotateProducer-> // 会对图片优化

DecodeProducer->

BitmapMemoryCacheProducer-> // 包含了cache的操作

BitmapMemoryCacheKeyMultiplexProducer-> // 从缓存读取的时候也是有优化的

ThreadHandoffProducer-> // 为任务分配线程

BitmapMemoryCacheGetProducer->

(PostprocessorProducer->PostprocessedBitmapMemoryCacheProducer)-> // 后处理bitmap,对Bitmap 的处理

(BitmapPrepareProducer) // 用来Andorid N 以后的优化

下面是所实现的所有Procuder,逻辑还是挺多挺复杂的,但是分成一个个看相应的逻辑还是很清晰的。

fresco中procuder
这里面有一个比较特殊的ProducerMultiplexProducer,用来将多个请求合并成一个请求。举一个例子,在某一段时间里面很有可能会去请求一张图片很多次,这样可能会导致创建多条”链子”,都需要网络请求,但是实际上只需要请求一次以后分别处理就好。怎么实现的呢,MultiplexProducer中有一个成员变量Map<K, Multiplexer> mMultiplexers用来保存每一个请求对应的Multiplexer(用来管理多个Consumer),Multiplexer里面的成员变量CopyOnWriteArraySet<Pair<Consumer<T>, ProducerContext>>代表的是该请求对应的所有的Consumer,当请求处理完成以后分别去处理集合里面的Consumer

还有一点很重要的是当相同的请求过来时是不会通知下面的Producer继续执行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void produceResults(Consumer<T> consumer, ProducerContext context) {
K key = getKey(context);
Multiplexer multiplexer;
do {
createdNewMultiplexer = false;
synchronized (this) {
multiplexer = getExistingMultiplexer(key);// 通过Key拿到Multiplexer
if (multiplexer == null) {
multiplexer = createAndPutNewMultiplexer(key);
createdNewMultiplexer = true;
}
}
} while (!multiplexer.addNewConsumer(consumer, context));// 将consumer 加入到multiplexer中去,相当于有多个Consumer 保存在里面
if (createdNewMultiplexer) {//从createdNewMultiplexer 赋值来看,后来加入的相同的multiplexer 是不会执行后面的Producer 的
multiplexer.startInputProducerIfHasAttachedConsumers();
}
}

Drawable 框架

fresco展示图片的逻辑也考虑的很充分,包括默认图、错误图、重试图、加载图、调试图片以及最终展示的图片。下面是fresco实现的Drawable,很明显的装饰模式。
fresco中Drawable实现

每次更新(设置Image、失败)的时候都需要更新View

(1).RootDrawable

(2).RoundedCornersDrawable

(3).FadeDrawable

(4).Drawable[]:代表的是一组Drawable

ScaleTypeDrawable:background

ScaleTypeDrawable:placeholer

ScaleTypeDrawable:actual image

:progress bar

:retry

:failure

上面是最终展示在SimpleDraweeView中的Drawable,当请求开始、请求中、请求结束的动作执行的时候只需要分别处理相应的Drawble(比如请求成功展示最终的图片)就好。

一个总结

fresco很强大,基本上能够完成图片加载的所有功能,特别适合含有特别多图片的应用。对内存也做了相应的优化。从UIL切换到fresco感觉工作量不是很大,现在如果从fresco切换到其它框架可能实现不了了,真担心被这个框架被绑架了😅。