Android 开源库分析——Fresco源码分析

基本介绍

包结构

  • drawee
  • fbcore
  • fresco
  • imagepipeline,Fresco的核心模块
  • memory
  • nativeimagefilters
  • nativeimagetranscoder

应用到的设计模式

从设计模式的角度来看Fresco,主要运用了如下设计模式

接口设计

  • Supplier<T>,简单的单类型对象提供者,实现该类后,通过该类就可以获取对应的对象;

  • DataSource

  • DataSubscriber

  • Producer

  • ProducerContext

  • Consumer

基本使用

使用很简单,第一步初始化Fresco:

1
Fresco.initialize(this);

加载网络图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 加载网络图片:方法1
// 网络图片地址
String imgUrl = "";
SimpleDraweeView simpleDraweeView = findViewById(R.id.xx);
simpleDraweeView.setImageURI(Uri.parse(url));

// 加载网络图片:方法2
String imgUrl = "";
SimpleDraweeView simpleDraweeView = findViewById(R.id.xx);
// 获取ImageRequest
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uri);
// 获取Controller
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
.setImageRequest(imageRequest)
.setOldController(draweeView.getController())
.setControllerListener(new BaseControllerListener<ImageInfo>())
.setAutoPlayAnimations(isSupportGif)
.build();
// 绑定Controller
simpleDraweeView.setController(draweeController);

加载Assets目录图片

不建议用Fresco加载本地图片,但它肯定是支持的(假设要加载的图片为assets目录下的a.png

1
2
3
SimpleDraweeView simpleDraweeView = findViewById(R.id.xx);
Uri uri = Uri.parse("asset:///" + "a.png");
simpleDraweeView.setImageURI(uri);

加载Drawable图片

1
2
3
SimpleDraweeView simpleDraweeView = findViewById(R.id.xx);
Uri uri = Uri.parse("res://" + packageName + drawableResId);
simpleDraweeView.setImageURI(uri);

加载本地文件中图片

1
2
3
SimpleDraweeView simpleDraweeView = findViewById(R.id.xx);
Uri uri = Uri.parse("file://" + filePath);
simpleDraweeView.setImageURI(uri);

加载ContentProvider中图片

1
2
3
SimpleDraweeView simpleDraweeView = findViewById(R.id.xx);
Uri uri = Uri.parse("content://" + filePath);
simpleDraweeView.setImageURI(uri);

图片的加载流程

  1. Fresco初始化;
  2. 获取DataSource
  3. 绑定Controller与Hierarchy
  4. 从内存缓存/磁盘缓存/网络获取图片,并最终设置到对应的Drawable层;

1.Fresco初始化

在开始使用前,Fresco需要进行初始化设置,即调用Fresco的initialize(Context)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void initialize(Context context, @Nullable ImagePipelineConfig imagePipelineConfig, @Nullable DraweeConfig draweeConfig, boolean useNativeCode) {
...
// 这里省略一些判断代码
NativeCodeSetup.setUseNativeCode(useNativeCode);
// 这里省略一些 NativeCode 初始化代码
context = context.getApplicationContext();
if (imagePipelineConfig == null) {
ImagePipelineFactory.initialize(context);
} else {
ImagePipelineFactory.initialize(imagePipelineConfig); // 初始化 ImagePipelineFactory
}

initializeDrawee(context, draweeConfig);// 初始化 Drawee
if (FrescoSystrace.isTracing()) {
FrescoSystrace.endSection();
}

}

private static void initializeDrawee(Context context, @Nullable DraweeConfig draweeConfig) {
...
sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);
// 传递一个DraweeControllerBuilder对象,然后通过该对象来获取DraweeController并设置到SimpleDraweeView中
SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
...
}

注意,以Supplier结尾的类都实现了Supplier接口,表明会提供一个对象,对象就是Supplier前面的内容。

上面的代码主要做了三件事:

  1. 完成了NativeLoader的初始化;
  2. 完成了ImagePipeline初始化;
  3. 完成了Drawee的初始化;

NativeLoader初始化

主要加载一些so库,如gif支持、webp支持,这个暂不做详细分析。

ImagePipeline初始化

ImagePipeline是通过ImagePipelineConfig来配置,通过ImagePipelineFactory的createImagePipeline()方法来创建的,ImagePipelineFactory中有三个静态成员,分别是:

1
2
3
private static ImagePipelineFactory sInstance = null;// ImagePipelineFactory 静态实例
private static boolean sForceSinglePipelineInstance;// 控制是否强制使用一个ImagePipeline实例
private static ImagePipeline sImagePipeline; // ImagePipeline 静态实例
ImagePipelineConfig

ImagePipelineConfigImagePipelineConfigInterface的具体实现类,ImagePipelineConfig 必须通过Builder模式来构造(构造方法是私有的),先看看接口都定义了那些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public interface ImagePipelineConfigInterface {
// 获取图片质量配置,默认ARGB_8888
Config getBitmapConfig();
// 获取Bitmap 内存缓存参数提供者,默认配置见 DefaultBitmapMemoryCacheParamsSupplier
Supplier<MemoryCacheParams> getBitmapMemoryCacheParamsSupplier();
// 获取Bitmap内存缓存的削减策略,即缓存大小达到了阈值以何种策略来处理缓存的更新,默认见 BitmapMemoryCacheTrimStrategy
CacheTrimStrategy getBitmapMemoryCacheTrimStrategy();
//
@Nullable
EntryStateObserver<CacheKey> getBitmapMemoryCacheEntryStateObserver();
// CacheKey生成工厂
CacheKeyFactory getCacheKeyFactory();
// Context
Context getContext();
// 磁盘缓存创建工厂
FileCacheFactory getFileCacheFactory();
// 是否开启向下采样
boolean isDownsampleEnabled();
// 是否开启磁盘缓存
boolean isDiskCacheEnabled();
// 获取未解码缓存参数提供者
Supplier<MemoryCacheParams> getEncodedMemoryCacheParamsSupplier();
// 获取线程池提供者
ExecutorSupplier getExecutorSupplier();
@Nullable
SerialExecutorService getExecutorServiceForAnimatedImages();
// 图片缓存状态追踪器
ImageCacheStatsTracker getImageCacheStatsTracker();
// 图片解码器
@Nullable
ImageDecoder getImageDecoder();
// 图像转码工厂
@Nullable
ImageTranscoderFactory getImageTranscoderFactory();
// 图像装吗类型
@Nullable
Integer getImageTranscoderType();
// 预加载是否开启配置提供者
Supplier<Boolean> getIsPrefetchEnabledSupplier();
// 磁盘缓存配置
DiskCacheConfig getMainDiskCacheConfig();
// 内存变化监听注册表
MemoryTrimmableRegistry getMemoryTrimmableRegistry();

int getMemoryChunkType();
// 网络图片的下载方式,默认采用的是 HttpUrlConnectionNetworkFetcher
NetworkFetcher getNetworkFetcher();
// 根据不同的 Android系统版本生成不同的Bitmap工厂,区别在于Bitmap分配在内存中的位置,Ashmem中,Android 5.0以上存在Java Heap中
@Nullable
PlatformBitmapFactory getPlatformBitmapFactory();
// 各种池的构建工厂(包括BitmapPool,MemoryChunkPool、ByteArrayPool等)
PoolFactory getPoolFactory();
// Jpeg 渐进式显示配置
ProgressiveJpegConfig getProgressiveJpegConfig();
// 网络请求监听
Set<RequestListener> getRequestListeners();
// 网络请求监听2
Set<RequestListener2> getRequestListener2s();
// 是否开启网络图片的resize和rotate
boolean isResizeAndRotateEnabledForNetwork();
// 小图磁盘缓存配置
DiskCacheConfig getSmallImageDiskCacheConfig();
// 图像解码器配置
@Nullable
ImageDecoderConfig getImageDecoderConfig();
// 调用者上下文验证器
@Nullable
CallerContextVerifier getCallerContextVerifier();
// ImagePipeline的实验性配置内容
ImagePipelineExperiments getExperiments();
//
CloseableReferenceLeakTracker getCloseableReferenceLeakTracker();
//
@Nullable
MemoryCache<CacheKey, CloseableImage> getBitmapCacheOverride();
//
@Nullable
MemoryCache<CacheKey, PooledByteBuffer> getEncodedMemoryCacheOverride();
//
BitmapMemoryCacheFactory getBitmapMemoryCacheFactory();
}

必要的配置选项Fresco都有默认的实现,如果需要定制,构造自己的ImagePipelineConfig传递进来即可。

ImagePipelineFactory

这个类的核心任务是创建ImagePipeline,先看看ImagePipeline是如何构造的,代码如下:

1
2
3
4
5
6
7
8
9
10
public ImagePipelineFactory(ImagePipelineConfigInterface config) {
...
this.mConfig = (ImagePipelineConfigInterface)Preconditions.checkNotNull(config); // ImagePipelineConfig 配置对象
this.mThreadHandoffProducerQueue = (ThreadHandoffProducerQueue)(this.mConfig.getExperiments().isExperimentalThreadHandoffQueueEnabled() ?
new ExperimentalThreadHandoffProducerQueueImpl(config.getExecutorSupplier().forLightweightBackgroundTasks()) : new ThreadHandoffProducerQueueImpl(config.getExecutorSupplier().forLightweightBackgroundTasks()));
CloseableReference.setDisableCloseableReferencesForBitmaps(config.getExperiments().getBitmapCloseableRefType());
this.mCloseableReferenceFactory = new CloseableReferenceFactory(config.getCloseableReferenceLeakTracker());
...

}

没啥,主要就是给几个变量赋值,包括ImagePipelineConfigThreadHandoffProducerQueueCloseableReferenceFactory,构造函数虽然简单,但是通过构造出来的ImagePipelineFactory可以获取到的对象就多了去了,包括:

  • DrawableFactory
  • CountingMemoryCache(即mBitmapCountingMemoryCache),内存缓存
  • InstrumentedMemoryCache(即mBitmapMemoryCache),内存缓存,提供缓存命中回调监听
  • CountingMemoryCache(即mEncodedCountingMemoryCache),内存缓存
  • InstrumentedMemoryCache(即mEncodedMemoryCache),内存缓存,提供缓存命中回调监听
  • BufferedDiskCache(即mMainBufferedDiskCache),带缓存的磁盘缓存
  • FileCache(即mMainFileCache),主文件缓存,磁盘文件缓存(这里主要是大文件)
  • ImagePipeline(即sImagePipeline),构建ImagePipeline
  • PlatformBitmapFactory(即mPlatformBitmapFactory),bitmap对象创建工程,主要提供不同平台版本下bitmap的创建方式
  • PlatformDecoder(即mPlatformDecoder),解码器,不同的平台、不同的Android系统版本采用不同的 Decoder
  • FileCache(即mSmallImageFileCache),小图文件缓存(可见小图的缓存和主文件缓存是分开的,避免共用缓存导致主文件缓存文件大侵占小文件缓存的情况
  • CloseableReferenceFactory(即mCloseableReferenceFactory),这个是在构造函数中赋值的

Drawee初始化

Drawee初始化的核心内容在PipelineDraweeControllerBuilderSupplier这个类上,从命名上看,该类会提供一个用来构建PipelineDraweeControllerPipelineDraweeControllerBuilder对象,看其构造函数。

PipelineDraweeControllerBuilder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public PipelineDraweeControllerBuilderSupplier(Context context, ImagePipelineFactory imagePipelineFactory, Set<ControllerListener> boundControllerListeners, Set<ControllerListener2> boundControllerListeners2, @Nullable DraweeConfig draweeConfig) {
this.mContext = context;// 获取context
this.mImagePipeline = imagePipelineFactory.getImagePipeline();// 获取ImagePipeline

// 获取 PipelineDraweeControllerFactory
if (draweeConfig != null && draweeConfig.getPipelineDraweeControllerFactory() != null) {
this.mPipelineDraweeControllerFactory = draweeConfig.getPipelineDraweeControllerFactory();
} else {
this.mPipelineDraweeControllerFactory = new PipelineDraweeControllerFactory();
}
// 调用 PipelineDraweeControllerFactory 初始化方法
this.mPipelineDraweeControllerFactory.init(context.getResources(), DeferredReleaser.getInstance(), imagePipelineFactory.getAnimatedDrawableFactory(context), UiThreadImmediateExecutorService.getInstance(), this.mImagePipeline.getBitmapMemoryCache(), draweeConfig != null ? draweeConfig.getCustomDrawableFactories() : null, draweeConfig != null ? draweeConfig.getDebugOverlayEnabledSupplier() : null);
this.mBoundControllerListeners = boundControllerListeners;
this.mBoundControllerListeners2 = boundControllerListeners2;
this.mDefaultImagePerfDataListener = draweeConfig != null ? draweeConfig.getImagePerfDataListener() : null;
}

构造函数主要进行了以下两个动作:

  1. 获取ImagePipeline;
  2. 获取PipelineDraweeControllerFactory并初始化PipelineDraweeControllerFactory相关参数,主要包括:
    1. Resources对象;
    2. DeferredReleaser.getInstance()延迟释放资源,等主线程处理完消息后再进行回收。
    3. mImagePipeline.getBitmapMemoryCache(),已经解码的Bitmap缓存

至此,图片加载流程中的Fresco初始化环节就完成了。这个初始化过程一般只需要调用一次即可,一般会在Application的onCreate中进行调用。

2.获取DataSource

什么是DataSource

在初始化完成后,就要看如何根据Url获取到显示的数据了,Fresco中数据由DataSource接口表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface DataSource<T> {
// 数据源是否关闭
boolean isClosed();
// 请求的结果
@Nullable
T getResult();
// 是否有结果返回
boolean hasResult();
// 获取一些额外的字段K-V表示
@Nullable
Map<String, Object> getExtras();
// 是否有多个请求结果
boolean hasMultipleResults();
// 请求是否结束
boolean isFinished();
// 请求是否失败
boolean hasFailed();
// 获取请求失败的原因
@Nullable
Throwable getFailureCause();
// 请求的进度(0到1)
float getProgress();
// 结束请求,释放资源
boolean close();
// 发送并订阅请求,等待请求结果
void subscribe(DataSubscriber<T> dataSubscriber, Executor executor);
}

DataSource有三种状态,采用枚举变量DataSourceStatus来表示,分别是IN_PROCESSSUCCESSFAILURE。基本实现类为AbstractDataSource,还有以下衍生类:

  • AbstractProducerToDataSourceAdapter,Producer到DataSource的适配(适配器模式);

  • CloseableProducerToDataSourceAdapter,实现了AbstractDataSourcecloseResult()方法;

  • ProducerToDataSourceAdapter,仅仅是AbstractProducerToDataSourceAdapter,无特殊方法实现;

  • FirstAvailableDataSource,之所以是叫FirstAvailable,主要是因为该类是FirstAvailableDataSourceSupplier的内部类,而FirstAvailableDataSourceSupplier内部有一个List<Supplier<DataSource<T>>> mDataSourceSuppliers的成员变量,可见其可以提供多个DataSource,我们使用的时候只取第一个可用的DataSource即可;

  • IncreasingQualityDataSource,内部维护一个CloseableProducerToDataSourceAdapter列表,按数据的清晰度从后往前递增,它为列表里的每个DataSourece绑定一个DataSubscriber,该类负责保证 每次获取清晰度更高的数据,获取数据的同时销毁清晰度更低的数据(HOW?)

    1
    2
    3
    4
    5
    6
    if (supplier != null && this.mLowResImageRequest != null) {
    List<Supplier<DataSource<IMAGE>>> suppliers = new ArrayList(2);
    suppliers.add(supplier); // index为0,正常质量
    suppliers.add(this.getDataSourceSupplierForRequest(controller, controllerId, this.mLowResImageRequest));// index为1,low质量
    supplier = IncreasingQualityDataSourceSupplier.create(suppliers, false);
    }
  • FirstAvailableDataSource:内部维护一个CloseableProducerToDataSourceAdapter列表,它会返回列表里最先获取数据的DataSource,它为列表里的每个DataSource绑定一个DataSubscriber,如果 数据加载成功,则将当前成功的DataSource指定为目标DataSource,否则跳转到下一个DataSource继续尝试。

  • SettableDataSource/SimpleDataSource:继承自AbstractDataSource,并将重写settResult()setFailure()setProgress()在内部调用父类的相应函数,但是修饰符变成了public(原来是protected)。即使 用SettableDataSource时可以在外部调用这三个函数设置DataSource状态。一般用于在获取DataSource失败时直接产生一个设置为Failure的DataSource。

DataSource是怎么生成的

SimpleDraweeView.setImageURI

基本使用中介绍了,初始化后调用SimpleDraweeViewsetImageUri(String url)方法就可以实现图片的加载与现实,那么这个是怎么做到的呢?看下面的分析:

1
2
3
4
5
6
7
8
public void setImageURI(Uri uri, @Nullable Object callerContext) {
DraweeController controller = this.mControllerBuilder
.setCallerContext(callerContext)// 设置调用的上下文对象
.setUri(uri)// 设置请求Uri,用来进行构造ImageRequest的Url
.setOldController(this.getController())// 设置OldController,主要用来进行Controller的复用判断
.build();
this.setController(controller);
}

setImageURI有多个重载,最终都会调用到上面这个方法。可以看到这里会用mControllerBuilder来构造一个DraweeController,然后设置给SimpleDraweeView。这个mControllerBuilder就是PipelineDraweeControllerBuilderSupplier.get()方法返回的PipelineDraweeControllerBuilder(继承自AbstractDraweeControllerBuilder,而该类实现了SimpleDraweeControllerBuilder接口),看看setUri(Uri uri)的最终实现:

1
2
3
4
5
6
7
8
9
10
11
public PipelineDraweeControllerBuilder setUri(@Nullable Uri uri) {
if (uri == null) {
return (PipelineDraweeControllerBuilder)super.setImageRequest((Object)null);
} else {
ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(uri)
.setRotationOptions(RotationOptions.autoRotateAtRenderTime())
.build();
return (PipelineDraweeControllerBuilder)super.setImageRequest(imageRequest);
}
}

setUri完成了图片请求ImageRequest的构造

PipelineDraweeControllerBuilder.obtainController()

再看看这个DraweeController是如何build的?build()方法最终会走到AbstractDraweeControllerbuildController()中:

1
2
3
4
5
6
protected AbstractDraweeController buildController() {
...
AbstractDraweeController controller = this.obtainController();
...
return controller;
}

obtainController()方法由AbstractDraweeController定义,由PipelineDraweeController实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected PipelineDraweeController obtainController() {
...
PipelineDraweeController var4;
try {
DraweeController oldController = this.getOldController();
// 获取到唯一的controllerId,一个静态自增的AtomicLong型: AtomicLong sIdCounter = new AtomicLong();
String controllerId = generateUniqueControllerId();
PipelineDraweeController controller;
// controller 复用逻辑
if (oldController instanceof PipelineDraweeController) {
controller = (PipelineDraweeController)oldController;
} else {
controller = this.mPipelineDraweeControllerFactory.newController();
}
// 关键所在,调用初始化方法,第一个参数就是我们关心的DataSource获取了。
controller.initialize(this.obtainDataSourceSupplier(controller, controllerId), controllerId, this.getCacheKey(), this.getCallerContext(), this.mCustomDrawableFactories, this.mImageOriginListener);
// 注册Controller回调
controller.initializePerformanceMonitoring(this.mImagePerfDataListener, this, Suppliers.BOOLEAN_FALSE);
var4 = controller;
} finally {
...
}
return var4;
}

这个方法里,看到了DataSource相关的内容了,因为Fresco的命名规则很规范,以Supplier结尾的表示实现了Supplier接口,其get方法会提供其前面类名的实例,这里的this.obtainDataSourceSupplier(...)说明会获取到DataSource实例,看controller.initialize(...)代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void initialize(Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, 
String id, CacheKey cacheKey, Object callerContext,
@Nullable ImmutableList<DrawableFactory> customDrawableFactories,
@Nullable ImageOriginListener imageOriginListener) {
...
super.initialize(id, callerContext);
this.init(dataSourceSupplier);
this.mCacheKey = cacheKey;
this.setCustomDrawableFactories(customDrawableFactories);
this.clearImageOriginListeners();
this.maybeUpdateDebugOverlay((CloseableImage)null);
this.addImageOriginListener(imageOriginListener);
...
}
PipelineDraweeControllerBuilder.obtainDataSourceSupplier

第一个参数由`this.obtainDataSourceSupplier(...)获取,看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected Supplier<DataSource<IMAGE>> obtainDataSourceSupplier(final DraweeController controller, 
final String controllerId) {
if (this.mDataSourceSupplier != null) { // 不为空,直接复用,这个mDataSourceSupplier会在this.init(dataSourceSupplier)里面赋值
return this.mDataSourceSupplier;
} else {
Supplier<DataSource<IMAGE>> supplier = null;
if (this.mImageRequest != null) { // mImageRequest在调用PipelineDraweeControllerBuilder的setUri方法时赋值
supplier = this.getDataSourceSupplierForRequest(controller, controllerId, this.mImageRequest);
} else if (this.mMultiImageRequests != null) {
supplier = this.getFirstAvailableDataSourceSupplier(controller, controllerId,
this.mMultiImageRequests, this.mTryCacheOnlyFirst);
}

if (supplier != null && this.mLowResImageRequest != null) {
List<Supplier<DataSource<IMAGE>>> suppliers = new ArrayList(2);
suppliers.add(supplier);
suppliers.add(this.getDataSourceSupplierForRequest(controller, controllerId, this.mLowResImageRequest));
supplier = IncreasingQualityDataSourceSupplier.create(suppliers, false);
}

if (supplier == null) {
supplier = DataSources.getFailedDataSourceSupplier(NO_REQUEST_EXCEPTION);
}

return (Supplier)supplier;
}
}

这里的IMAGE泛型指的是CloseableReference<CloseableImage>类型,后面会提及。具体代码分析见注释。上面获取Supplier的一系列getXX方法最终都会走到PipelineDraweeControllerBuildergetDataSourceForRequest中,看看这个getDataSourceForRequest方法(由PipelineDraweeControllerBuilder实现)

PipelineDraweeControllerBuilder.getDataSourceForRequest
1
2
3
protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(DraweeController controller, String controllerId, ImageRequest imageRequest, Object callerContext, CacheLevel cacheLevel) {
return this.mImagePipeline.fetchDecodedImage(imageRequest, callerContext, convertCacheLevelToRequestLevel(cacheLevel), this.getRequestListener(controller), controllerId);
}

结果就是调用ImagePipelinefetchDecodedImage,mImagePipeline是通过ImagePipelineFactory来进行赋值的(在PipelineDraweeControllerBuilderSupplier构造的时候),那么这个fetchDecodedImage干了什么呢?

ImagePipeline.submitFetchRequest
1
2
3
4
5
6
7
8
public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(ImageRequest imageRequest, Object callerContext, RequestLevel lowestPermittedRequestLevelOnSubmit, @Nullable RequestListener requestListener, @Nullable String uiComponentId) {
try {
Producer<CloseableReference<CloseableImage>> producerSequence = this.mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
return this.submitFetchRequest(producerSequence, imageRequest, lowestPermittedRequestLevelOnSubmit, callerContext, requestListener, uiComponentId);
} catch (Exception var7) {
return DataSources.immediateFailedDataSource(var7);
}
}

这个方法先获取Producer,然后调用submitFetchRequest方法来拉取图片请求,生成DataSource并最终返回,所以DataSource最终是有ImagePipeline调用submitFetchRequest方法生成的。

看看这个最终生成DataSource的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private <T> DataSource<CloseableReference<T>> submitFetchRequest(Producer<CloseableReference<T>> producerSequence, ImageRequest imageRequest, RequestLevel lowestPermittedRequestLevelOnSubmit, Object callerContext, @Nullable RequestListener requestListener, @Nullable String uiComponentId) {
...
DataSource var9;
try {
RequestLevel lowestPermittedRequestLevel = RequestLevel.getMax(imageRequest.getLowestPermittedRequestLevel(), lowestPermittedRequestLevelOnSubmit);
SettableProducerContext settableProducerContext = new SettableProducerContext(imageRequest, this.generateUniqueFutureId(), uiComponentId, requestListener2, callerContext, lowestPermittedRequestLevel, false, imageRequest.getProgressiveRenderingEnabled() || !UriUtil.isNetworkUri(imageRequest.getSourceUri()), imageRequest.getPriority(), this.mConfig);
DataSource var10 = CloseableProducerToDataSourceAdapter.create(producerSequence, settableProducerContext, requestListener2);
return var10;
} catch (Exception var14) {
var9 = DataSources.immediateFailedDataSource(var14);
} finally {
...
}

return var9;
}

主要完成以下工作:

  1. 获取RequestLevel,即请求的级别,分别有FULL_FETCH(1)DISK_CACHE(2)ENCODED_MEMORY_CACHE(3),BITMAP_MEMORY_CACHE(4),对应的时从网络或本地获取、从磁盘缓存获取、从未解码的内存缓存获取、从已解码的缓存获取。
  2. 获取SettableProducerContext,获取Producer运行的上下文环境,通过ProducerContext可以控制Producer的内部状态。
  3. 创建DataSource(CloseableProducerToDataSourceAdapter是DataSource的一种)并返回。

DataSource的获取过程就到这了,它创建后就会被设置到PipelineDraweeController中。下面看看Controller和Hierarchy的绑定。

3.绑定Controller与Hierarchy

DraweeHolder.setController()

上面说了,在调用SimpleDraweeView的setImageURI()方法时,创建了DraweeController(即PipelineDraweeControllerBuilder),这个DraweeController完成了DataSource的获取,在该Controller获取后,SimpleDraweeView立马调用自身的setController方法来讲这个Controller设置进去,这个setController方法实际上调用的时DraweeHolder的setController方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// SimpleDraweeView父类DraweeView的setController方法
public void setController(@Nullable DraweeController draweeController) {
this.mDraweeHolder.setController(draweeController);
super.setImageDrawable(this.mDraweeHolder.getTopLevelDrawable());
}
// DraweeHolder的setController方法,mDraweeHolder是在DraweeView初始化时创建的
public void setController(@Nullable DraweeController draweeController) {
boolean wasAttached = this.mIsControllerAttached;
if (wasAttached) { // 如果绑定了,先解绑
this.detachController();
}

if (this.isControllerValid()) {// 先将之前的置null
this.mEventTracker.recordEvent(Event.ON_CLEAR_OLD_CONTROLLER);
this.mController.setHierarchy((DraweeHierarchy)null);
}

this.mController = draweeController;// 重设Controller
if (this.mController != null) {
this.mEventTracker.recordEvent(Event.ON_SET_CONTROLLER);
this.mController.setHierarchy(this.mHierarchy);// 重设Hierarchy
} else {
this.mEventTracker.recordEvent(Event.ON_CLEAR_CONTROLLER);
}

if (wasAttached) {// 重绑Controller
this.attachController();
}
}
  1. 如果Controller已经绑定了,先解绑;
  2. 如果Hierarchy已经绑定到了Controller,先解绑;
  3. 重设Hierarchy;
  4. 重设Controller

PipelineDraweeController.setHierarchy()

关于HierarchyController的重设,Hierarchy的重设最终调用的是PipelineDraweeControllersetHierarchy方法,参数Hierarchy是在GenericDraweeViewinflateHierarchy方法中赋值的,看PipelineDraweeControllersetHierarchy方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void setHierarchy(@Nullable DraweeHierarchy hierarchy) {
...
if (this.mIsRequestSubmitted) {
this.mDeferredReleaser.cancelDeferredRelease(this);
this.release();
}

if (this.mSettableDraweeHierarchy != null) {
this.mSettableDraweeHierarchy.setControllerOverlay((Drawable)null);
this.mSettableDraweeHierarchy = null;
}

if (hierarchy != null) {
Preconditions.checkArgument(hierarchy instanceof SettableDraweeHierarchy);
this.mSettableDraweeHierarchy = (SettableDraweeHierarchy)hierarchy;
this.mSettableDraweeHierarchy.setControllerOverlay(this.mControllerOverlay);
}

if (this.mLoggingListener != null) {
this.setUpLoggingListener();
}

}

上面方法主要完成三个动作:

  1. 如果当前存在正在进行的请求,取消并释放掉;
  2. 清空已经存在的Hierarchy;
  3. 设置新的Hierarchy;

同样的,setController中的attachController其实最终调用的AbstractDraweeController的onAttach方法,该方法中工作:取消重在进行的请求,打上attach标记,重新提交请求;

这样Controller和Hierarchy的绑定工作就完成了,完成Controller和Hierarchy的绑定后,图片最终怎么显示出来呢?看下面。

4.获取图片并设置到对应的Drawable层

SimpleDraweeView.setImageURI发起,然后构造DraweeController,由Controller来完成请求(ImageRequest)的构造、发起(submitRequest)与请求结果的处理(onNewResultInternal),并最调用GenericDraweeHierarch的setImage方法完成图片的显示。

图片的最终设置,是调用GenericDraweeHierarchy的setImage方法来实现的,该方法是在AbstractDraweeController中submitRequest中调用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void setImage(Drawable drawable, float progress, boolean immediate) {
drawable = WrappingUtils.maybeApplyLeafRounding(drawable, this.mRoundingParams, this.mResources);
drawable.mutate();
this.mActualImageWrapper.setDrawable(drawable);
this.mFadeDrawable.beginBatchMode();
this.fadeOutBranches();
this.fadeInLayer(2);
this.setProgress(progress);
if (immediate) {
this.mFadeDrawable.finishTransitionImmediately();
}

this.mFadeDrawable.endBatchMode();
}

mActualImageWrapper就是实际加载图片的那个图层,此处要设置的SimpleDraweeView最终要显示的图片。这样SimpleDraweeView的图片加载流程就完成了,当然还有不少细节需要继续分析。

Producer与Consumer

Fresco源码很好的利用了生产者和消费者模式,其中Producer负责从不同的来源(本地、网络等)获取图片数据,然后交由Consumer来消费处理,Producer的创建时机是在ImagePipeline调用fetchEncodedImagefetchDecodedImage中,在调用ImagePipelinesubmitFetchRequest之前,需要先构建好ProducerProducer是由ProducerSequenceFactory的来进行构建的。

Producer

Producer接口的实现类有多种,按加载资源的种类可以分为以下几种:

1
2
3
public interface Producer<T> {
void produceResults(Consumer<T> consumer, ProducerContext context);
}

获取本地资源类Producer

从本地资源中获取数据

  • LocalFetchProducer,本地资源类Producer的基类;提供一个抽象方法供其子类实现,就是根据自己的实际情况获取EncodedImage对象:
1
protected abstract EncodedImage getEncodedImage(ImageRequest imageRequest) throws IOException;
  • DataFetchProducer,这个虽然继承自LocalFetchProducer,但不是严格意义上的本地资源,可以是一个Base64加密的图片资源字符串或者其他形式的加密串;
  • LocalAssetFetchProducer,继承自LocalFetchProducer,从Asset里面的图片资源获取
  • LocalContentUriFetchProducer,继承自LocalFetchProducer,从ContentUri形式的图片资源获取
  • LocalContentUriThumbnailFetchProducer,继承自LocalFetchProducer,从ContentUrlThumbnail 形式的图片资源获取LocalFileFetchProducer,继承自LocalFetchProducer,从本地文件中的图片资源获取
  • LocalResourceFetchProducer,继承自LocalFetchProducer,从本地res文件夹下的图片资源
  • QualifierResourceFetchProducer,继承自LocalFetchProducer,从ContentResolver获取本地图片资源
  • LocalExifThumbnailProducer,没继承LocalFetchProducer,获取本地Exif格式的图片
  • LocalVideoThumbnailProducer,没有继承LocalFetchProducer,获取本地视频的缩略图;

获取网络数据类Producer

主要负责从网络层获取数据

  • NetworkFetchProducer,实现Producer接口,从网络获取图片数据;

缓存类Producer

主要负责从缓存中获取数据

  • BitmapMemoryCacheGetProducer 它是一个Immutable的Producer,仅用于包装后续Producer;
  • BitmapMemoryCacheProducer 在已解码的内存缓存中获取数据;若未找到,则在nextProducer中获取数据,并在获取到数据的同时将其缓存;
  • BitmapMemoryCacheKeyMultiplexProducer 是MultiplexProducer的子类,nextProducer为BitmapMemoryCacheProducer,将多个拥有相同已解码内存缓存键的ImageRequest进行“合并”,若缓存命中,它们都会获取到该数据;
  • PostprocessedBitmapMemoryCacheProducer 在已解码的内存缓存中寻找PostProcessor处理过的图片。它的nextProducer都是PostProcessorProducer,因为如果没有获取到被PostProcess的缓存,就需要对获取的图片进行PostProcess。;若未找到,则在nextProducer中获取数据;
  • EncodedMemoryCacheProducer 在未解码的内存缓存中寻找数据,如果找到则返回,使用结束后释放资源;若未找到,则在nextProducer中获取数据,并在获取到数据的同时将其缓存;
  • EncodedCacheKeyMultiplexProducer 是MultiplexProducer的子类,nextProducer为EncodedMemoryCacheProducer,将多个拥有相同未解码内存缓存键的ImageRequest进行“合并”,若缓存命中,它们都会获取到该数据;
  • DiskCacheProducer 在文件内存缓存中获取数据;若未找到,则在nextProducer中获取数据,并在获取到数据的同时将其缓存

功能类Producer

该类Producer在构建的时候回传入一个inputProducer,然后改Producer会对inputProducer进行相应的处理

  • MultiplexProducer 将多个拥有相同CacheKey的ImageRequest进行“合并”,让他们从都从inputProducer中获取数据;
  • ThreadHandoffProducer 将inputProducer的produceResult方法放在后台线程中执行(线程池容量为1);
  • SwallowResultProducer 将inputProducer的获取的数据“吞”掉,会在Consumer的onNewResult中传入null值;
  • ResizeAndRotateProducer 将inputProducer产生的EncodedImage根据EXIF的旋转、缩放属性进行变换(如果对象不是JPEG格式图像,则不会发生变换);
  • PostProcessorProducer 将inputProducer产生的EncodedImage根据PostProcessor进行修改,关于PostProcessor详见修改图片;
  • DecodeProducer 将inputProducer产生的EncodedImage解码。解码在后台线程中执行,可以在ImagePipelineConfig中通过setExecutorSupplier来设置线程池数量,默认为最大可用的处理器数;
  • WebpTranscodeProducer 若inputProducer产生的EncodedImage为WebP格式,则将其解码成DecodeProducer能够处理的EncodedImage。解码在后代进程中进行。

ProducerSequenceFactory

意为Producer 序列工厂,所以ProducerSequenceFactory用于获取Producer序列,这里序列指的是通过对Producer一层层的包装,从外到里一层层包装,最里层Producer处理后,交给外层处理,即Producer是由外向里一层层传递的。

  • PostprocessedBitmapMemoryCacheProducer,非必须 ,在Bitmap缓存中查找被PostProcess过的数据。
  • PostprocessorProducer,非必须,对下层Producer传上来的数据进行PostProcess。
  • BitmapMemoryCacheGetProducer,必须,使Producer序列只读。
  • ThreadHandoffProducer,必须,使下层Producer工作在后台进程中执行。
  • BitmapMemoryCacheKeyMultiplexProducer,必须,使多个相同已解码内存缓存键的ImageRequest都从相同Producer中获取数据。
  • BitmapMemoryCacheProducer,必须,从已解码的内存缓存中获取数据。
  • DecodeProducer,必须,将下层Producer产生的数据解码。
  • ResizeAndRotateProducer,非必须,将下层Producer产生的数据变换。
  • EncodedCacheKeyMultiplexProducer,必须,使多个相同未解码内存缓存键的ImageRequest都从相同Producer中获取数据。
  • EncodedMemoryCacheProducer,必须,从未解码的内存缓存中获取数据。
  • DiskCacheProducer,必须,从文件缓存中获取数据。
  • WebpTranscodeProducer,非必须,将下层Producer产生的Webp(如果是的话)进行解码。
  • NetworkFetchProducer,必须,从网络上获取数据。

Consumer

Producer经过一些列的传递后,最终肯定会调用produceResult方法,然后在produceResult方法中调用Consumer来对产生的结果进行“消费”,那么Consumer是在什么时候创建的呢?

AbstractProducerToDataSourceAdapter的构造函数中,会调用createConsumer方法来创建Consumer,在请求经过Producer一层层由外到里传递后,到最里层最终会调用Consumer的onNewResultImpl来消费产生的结果,并回调给上层。

缓存

Fresco是三级缓存结构,包括二级内存缓存,一级磁盘缓存,其中两级内存缓存包括:

  • 已解码的内存缓存,由MemoryCache 类对象表示;
  • 未解码的内存缓存,由MemoryCache 类对象表示;

二者的区别:

已解码内存缓存 未解码内存缓存
缓存对象 CloseableImage(CloseableBitmap、CloseableStaticBitmap) EncodedImage
对应的Producer BitmapMemoryCacheProducer EncodedMemoryCacheProducer

解码内存缓存流程

解码内存缓存由BitmapMemoryCacheProducer实现,主要在produceResult方法中,有以下几步:

  1. 从 MemoryCache 中 根据 CacheKey去取缓存;
  2. 判断缓存是否符合条件,符合条件的话,调用Consumer消费,直接反回,这个图片请求结束;
  3. 不符合条件,则将原始的consumer包装,得到一个wrapperConsumer,然后继续调用mInputProducer进行处理;
  4. 在mInputProducer的produceResult方法中,会调用wrapperConsumer来消费,在wrapperConsumer的onNewResultImpl中按需将请求得到的newResult(CloseableReference对象)进行缓存;
  5. 得到包装之前的Consumer,调用onNewResult通知上层Producer图片请求的结果

未解码内存缓存流程

未解码内存缓存由EncodedMemoryCacheProducer实现,主要流程和解码内存缓存相似,不在赘述。

内存缓存的实现

有MemoryCache接口来实现,具体的实现类是LruCountingMemoryCache,里面有两个缓存集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LruCountingMemoryCache<K, V>
implements CountingMemoryCache<K, V>, MemoryCache<K, V>, HasDebugData {
...
final CountingLruMap<K, Entry<K, V>> mExclusiveEntries;
final CountingLruMap<K, Entry<K, V>> mCachedEntries;
...

public LruCountingMemoryCache(
ValueDescriptor<V> valueDescriptor,
CacheTrimStrategy cacheTrimStrategy,
Supplier<MemoryCacheParams> memoryCacheParamsSupplier,
@Nullable EntryStateObserver<K> entryStateObserver) {
...
mExclusiveEntries = new CountingLruMap<>(wrapValueDescriptor(valueDescriptor));
mCachedEntries = new CountingLruMap<>(wrapValueDescriptor(valueDescriptor));
...
}

其中:

  • mCachedEntries,它是用来存放所有缓存对象的集合;
  • mExclusiveEntries,它主要用来存放当前没有被引用的对象,在缓存控件不足时,发生trim会删除掉该集合中的对象来释放缓存空间;

加入缓存:cache()

具体的缓存方法时cache,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public @Nullable CloseableReference<V> cache(final K key, final CloseableReference<V> valueRef,
final @Nullable EntryStateObserver<K> observer) {
...
Entry<K, V> oldExclusive;
CloseableReference<V> oldRefToClose = null;
CloseableReference<V> clientRef = null;
synchronized (this) {
// remove the old item (if any) as it is stale now
oldExclusive = mExclusiveEntries.remove(key);// 从未被引用的集合中移除,因为现在要被引用了
Entry<K, V> oldEntry = mCachedEntries.remove(key);// 从已经缓存的集合中移除,因为现在更新key对应的缓存值了
if (oldEntry != null) {
makeOrphan(oldEntry);
oldRefToClose = referenceToClose(oldEntry); // 得到待删除的oldEntry
}

if (canCacheNewValue(valueRef.get())) { // 如果能进行缓存操作
Entry<K, V> newEntry = Entry.of(key, valueRef, observer);// 构建新的Entry
mCachedEntries.put(key, newEntry);// 加入缓存
clientRef = newClientReference(newEntry);// 得到引用以便返回
}
}
CloseableReference.closeSafely(oldRefToClose);// close掉之前缓存的救治
maybeNotifyExclusiveEntryRemoval(oldExclusive);// 通知未引用集合可能改变

maybeEvictEntries();// 通知缓存集合可能改变,需要调整大小
return clientRef;
}

cache的流程注释很清楚了,这里就不在一一说明了。

获取缓存:get()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  public CloseableReference<V> get(final K key) {
Preconditions.checkNotNull(key);
Entry<K, V> oldExclusive;
CloseableReference<V> clientRef = null;
synchronized (this) {
oldExclusive = mExclusiveEntries.remove(key);// 从未被引用的集合中移除,因为现在要被引用了
Entry<K, V> entry = mCachedEntries.get(key);// 根据key获取缓存的Entry
if (entry != null) {
clientRef = newClientReference(entry);// 得到包装后的对象
}
}
maybeNotifyExclusiveEntryRemoval(oldExclusive);
maybeUpdateCacheParams();// 调整缓存参数设置
maybeEvictEntries();// 通知缓存集合可能改变,需要调整大小
return clientRef;
}

缓存get的流程页比较简单。

磁盘缓存流程

磁盘缓存由于涉及到文件的读写,又包括以下几个过程:

  • 缓冲缓存层,由BufferedDiskCache实现,提供缓冲功能;
  • 文件缓存层,由DiskStorageCache实现,提供实际的缓存功能;
  • 文件存储层,有DefaultDiskStorage实现,提供磁盘文件读写功能;

磁盘缓存存的过程

DiskCacheWriteProducer

磁盘缓存主要是通过DiskCacheWriteProducer来完成的,缓存的具体实现是通过BufferedDiskCache来完成的,看下该Producer中定义的Consumer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private static class DiskCacheWriteConsumer
extends DelegatingConsumer<EncodedImage, EncodedImage> {
...
@Override
public void onNewResultImpl(@Nullable EncodedImage newResult, @Status int status) {
mProducerContext.getProducerListener().onProducerStart(mProducerContext, PRODUCER_NAME);
// intermediate, null or uncacheable results are not cached, so we just forward them
// as well as the images with unknown format which could be html response from the server
if (isNotLast(status)
|| newResult == null
|| statusHasAnyFlag(status, DO_NOT_CACHE_ENCODED | IS_PARTIAL_RESULT)
|| newResult.getImageFormat() == ImageFormat.UNKNOWN) { // 不符合缓存条件的,不予缓存
mProducerContext
.getProducerListener()
.onProducerFinishWithSuccess(mProducerContext, PRODUCER_NAME, null);
getConsumer().onNewResult(newResult, status);
return;
}

final ImageRequest imageRequest = mProducerContext.getImageRequest();// 得到请求
final CacheKey cacheKey =
mCacheKeyFactory.getEncodedCacheKey(imageRequest, mProducerContext.getCallerContext());// 根据请求获取到CacheKey

if (imageRequest.getCacheChoice() == ImageRequest.CacheChoice.SMALL) {// 按需缓存
mSmallImageBufferedDiskCache.put(cacheKey, newResult);
} else {
mDefaultBufferedDiskCache.put(cacheKey, newResult);
}
mProducerContext
.getProducerListener()
.onProducerFinishWithSuccess(mProducerContext, PRODUCER_NAME, null);// 结果回调出去

getConsumer().onNewResult(newResult, status);// 交给上层Consumer处理
}
}
BufferedDiskCache

该类是磁盘缓存的缓冲层,其成员包括用于文件存储的mFileCache,用于读写缓存的mReadExecutor和mWriteExecutor,用于保存当前正在进行缓存操作的mStagingArea。

BufferedDiskCache.put()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void put(final CacheKey key, EncodedImage encodedImage) {
try {
...
// 现将要缓存的内容加入到Staging区域
mStagingArea.put(key, encodedImage);
final EncodedImage finalEncodedImage = EncodedImage.cloneOrNull(encodedImage);
try {
final Object token = FrescoInstrumenter.onBeforeSubmitWork("BufferedDiskCache_putAsync");
mWriteExecutor.execute(
new Runnable() {
@Override
public void run() {
final Object currentToken = FrescoInstrumenter.onBeginWork(token, null);
try {
writeToDiskCache(key, finalEncodedImage);// 开启线程写入到DiskStorageCache中
} catch (Throwable th) {
FrescoInstrumenter.markFailure(token, th);
throw th;
} finally {
mStagingArea.remove(key, finalEncodedImage);// 从Staging区域移除
EncodedImage.closeSafely(finalEncodedImage);// 关闭资源
FrescoInstrumenter.onEndWork(currentToken);
}
}
});
} catch (Exception exception) {
...
mStagingArea.remove(key, encodedImage);// 从Staging区域移除
EncodedImage.closeSafely(finalEncodedImage); // 关闭资源
}
} finally {
...
}
}
BufferedDiskCache.writeToDiskCache()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void writeToDiskCache(final CacheKey key, final EncodedImage encodedImage) {
...
mFileCache.insert(
key,
new WriterCallback() {
@Override
public void write(OutputStream os) throws IOException {
InputStream inputStream = encodedImage.getInputStream();
Preconditions.checkNotNull(inputStream);
mPooledByteStreams.copy(inputStream, os);
}
});
mImageCacheStatsTracker.onDiskCachePut(key);
...
}

核心代码就是将要缓存的内容插入到mFileCache中,mFileCache为DiskStorageCache实例。

DiskStorageCache.insert()

DiskStorageCache完成了文件实际缓存功能,而整个insert的过程代码比较多,主要包括以下几个步骤:

  1. 根据缓存的CacheKey得到resourceId

    resourceId = CacheKeyUtil.getFirstResourceId(key);

  2. 调用DefaultDiskStorage的startInsert方法创建临时文件,并包装成DiskStorage.Inserter返回;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public Inserter insert(String resourceId, Object debugInfo) throws IOException {
    // ensure that the parent directory exists
    FileInfo info = new FileInfo(FileType.TEMP, resourceId);
    File parent = getSubdirectory(info.resourceId);
    if (!parent.exists()) {
    mkdirs(parent, "insert");
    }

    try {
    File file = info.createTempFile(parent);
    return new InserterImpl(resourceId, file);
    } catch (IOException ioe) {
    mCacheErrorLogger.logError(
    CacheErrorLogger.CacheErrorCategory.WRITE_CREATE_TEMPFILE, TAG, "insert", ioe);
    throw ioe;
    }
    }
  3. 调用DiskStorage.Inserter的writeData方法将图片内容写到临时文件中。

    1
    2
    3
    4
    5
    6
    7
    public void writeData(WriterCallback callback, Object debugInfo) throws IOException {
    FileOutputStream fileStream = new FileOutputStream(mTemporaryFile);
    ...
    CountingOutputStream countingStream = new CountingOutputStream(fileStream);
    callback.write(countingStream);
    countingStream.flush();
    }
  4. 调用DiskStorageCache的endInsert,方法内部会通过DiskStorage.Inserter的commit方法,将临时文件重命名为resourceId;

  5. 重命名成功后,会工薪缓存的修改时间;

这样整个图片缓存的insert就完成了。

磁盘缓存读的过程

磁盘缓存读的过程其实就是磁盘缓存写的过程的逆过程

DiskCacheReadProducer

DiskCacheReadProducer中利用的开源的bolts-tasks-1.4.0这个库,类似于RxJava,但是比RxJava更轻便,支持链式编程。produceResults方法中,根据请求获取缓存的CacheKey和缓存配置,然后就是缓存读取成功后的处理,核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
final CacheKey cacheKey =
mCacheKeyFactory.getEncodedCacheKey(imageRequest, producerContext.getCallerContext());// 获取cacheKey
final boolean isSmallRequest = (imageRequest.getCacheChoice() == CacheChoice.SMALL);// 获取缓存类型
final BufferedDiskCache preferredCache =
isSmallRequest ? mSmallImageBufferedDiskCache : mDefaultBufferedDiskCache;
final AtomicBoolean isCancelled = new AtomicBoolean(false);
final Task<EncodedImage> diskLookupTask = preferredCache.get(cacheKey, isCancelled);// 开始一个task获取缓存
final Continuation<EncodedImage, Void> continuation =
onFinishDiskReads(consumer, producerContext);// 创建一个后续task,该task在diskLookupTask完成后调用
diskLookupTask.continueWith(continuation);
subscribeTaskForRequestCancellation(isCancelled, producerContext);

缓存的获取部分代码:

BufferedDiskCache.get()

1
2
3
4
5
6
7
8
9
10
11
12
public Task<EncodedImage> get(CacheKey key, AtomicBoolean isCancelled) {
try {
...
final EncodedImage pinnedImage = mStagingArea.get(key);// 在StagingArea中找
if (pinnedImage != null) {
return foundPinnedImage(key, pinnedImage);
}
return getAsync(key, isCancelled);// StagingAream找不到就异步在磁盘中找
} finally {
...
}
}

BufferedDiskCache.getAsync()

getSsync()步骤:

  1. 同样是在StagingArea中找;
  2. 找不到就调用readFromDiskCache从磁盘中找;
  3. readFromDiskCache最终调用DiskStorageCache.getResource(key)找到BinaryResource资源,然后通过BinaryResource来获取其二进制流返回;

以上就是读取磁盘缓存的大概过程。

小知识

  1. Fresco默认内存缓存的大小

    设置间下面代码:

    DefaultBitmapMemoryCacheParamsSupplier.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    private int getMaxCacheSize() {
    final int maxMemory =
    Math.min(mActivityManager.getMemoryClass() * ByteConstants.MB, Integer.MAX_VALUE);
    if (maxMemory < 32 * ByteConstants.MB) {
    return 4 * ByteConstants.MB;
    } else if (maxMemory < 64 * ByteConstants.MB) {
    return 6 * ByteConstants.MB;
    } else {
    // We don't want to use more ashmem on Gingerbread for now, since it doesn't respond well to
    // native memory pressure (doesn't throw exceptions, crashes app, crashes phone)
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    return 8 * ByteConstants.MB;
    } else {
    return maxMemory / 4;// 现在绝大多数命中这个分支了
    }
    }
    }
  2. 使用Fresco显示图片时SimpleDraweeView需要明确图片的显示尺寸,要么指定为MatchParent,要么指定宽和高;

  3. Fresco自带的高斯模糊处理

    注意:这里的图片是非加密的图片,Fresco自带的高斯模糊处理有两种:

    • BlurPostProcessor,纯Java实现;
    • IterativeBoxBlurPostProcessor,采用C实现,省内存更高效,性能更好
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public static void showBlurImg(@NonNull DraweeView draweeView, String url, 
    int iterations, int blurRadius) {
    try {
    Uri uri = Uri.parse(url);
    ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
    .setPostprocessor(new IterativeBoxBlurPostProcessor(iterations, blurRadius))
    .build();
    DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setOldController(draweeView.getController())
    .setImageRequest(imageRequest)
    .build();
    draweeView.setController(controller);
    } catch (Exception e) {
    LogHelper.printStackTrace(e);
    }
    }
  4. Fresco Bitmap对象的获取

    1. 方法一:获取已经解密的Bitmap对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    /**
    * 加载图片成bitmap。
    *
    * @param imageUrl 图片地址。
    */
    public static void loadToBitmap(String imageUrl, BaseBitmapDataSubscriber mDataSubscriber) {
    ImageRequest imageRequest = ImageRequestBuilder
    .newBuilderWithSource(Uri.parse(imageUrl))
    .setProgressiveRenderingEnabled(true)
    .build();

    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage
    (imageRequest, App.get());
    dataSource.subscribe(mDataSubscriber, CallerThreadExecutor.getInstance());
    }

    // 然后这样调用这个方法
    loadToBitmap(imageUrl, new BaseBitmapDataSubscriber() {
    @Override
    public void onNewResultImpl(@Nullable Bitmap bitmap) {
    // 读取成功。
    }

    @Override
    public void onFailureImpl(DataSource dataSource) {
    // 读取失败。
    }
    });
    1. 方法二,直接利用当前展示的Bitmap对象(这个一定是解密后的Bitmap)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(uri)
      .setImageDecodeOptions(imageDecodeOptions)
      .setRotationOptions(RotationOptions.autoRotate())
      .build();

      DraweeController controller = Fresco.newDraweeControllerBuilder()
      .setImageRequest(imageRequest)
      .setOldController(draweeView.getController())
      .setControllerListener(new BaseControllerListener<Object>() {
      @Override
      public void onFinalImageSet(String id, @Nullable Object imageInfo,
      @Nullable Animatable animatable) {
      super.onFinalImageSet(id, imageInfo, animatable);
      // 这里的imageInfo就是解密之后的 CloseableStaticBitmap 对象,
      if (imageInfo instanceof CloseableStaticBitmap) {
      CloseableStaticBitmap staticBitmap = (CloseableStaticBitmap) imageInfo;
      // 获取到真正的bitmap
      Bitmap bitmap = staticBitmap.getUnderlyingBitmap();
      }
      }
      }
      .build();
      draweeView.setController(controller);
  5. Fresco 图片的下载

    主要就是调用prefetchToDiskCache将图片保存在磁盘缓存中,然后DataSubscriber去订阅即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    DataSource<Boolean> dataSource;
    ImageRequest imageRequest = ImageRequest.fromUri(Uri.parse(url));
    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    dataSource = imagePipeline.isInDiskCache(imageRequest);
    DataSubscriber<Boolean> subscriber = new BaseDataSubscriber<Boolean>() {
    @Override
    protected void onNewResultImpl(DataSource<Boolean> dataSource) {
    if (!dataSource.isFinished()) {
    return;
    }
    if (listener != null) {
    listener.handleResult(dataSource.getResult());
    }
    // your code here
    dataSource = null;
    }

    @Override
    protected void onFailureImpl(DataSource<Boolean> dataSource) {
    if (listener != null) {
    listener.handleResult(false);
    }
    }
    };
    dataSource.subscribe(subscriber, CallerThreadExecutor.getInstance());

总结

本文简单的介绍了下Fresco的基本应用,并结合代码分析了期图片的加载流程,抛开框架本身强大的功能不谈,Fresco在代码设计的层面给人的启示就很深刻,大量的应用了设计模式,合理的进行了分层和解耦,是以后代码设计的一个很好的参考。

PS:经过这么长的时间,这篇文章总算完成了,希望自己以后能更加高效一点,更有执行力一点。

参考文章:

  1. Android开源框架源码鉴赏:Fresco