Fresco 源码分析(三) Fresco服务端处理(1) ImagePipeline为何物

4.3 服务端的处理#

备注: 因为是分析,而不是设计,所以很多知识我们类似于插叙的方式叙述,就是用到了哪个知识点,我们再提及相关的知识点,如果分析到了最后,我想想是不是应该将这个架构按照设计的方式,重新梳理一下(套用一句话,现在安卓的应用其实很多都像是快餐,至今面试了应该有40多位的安卓程序员,知道框架的很多,会用的也很多,会总结和整理的却没有几个,想到安卓程序员薪资超过20K的并不多,个人感觉因为很多都只是会简单的UI层的处理,稍微设计到业务逻辑层,即设计一套机制的时候,便卡顿了,这应该算是瓶颈吧)

插叙: 其实在客户端与服务端的交互中,还设计到了一个知识点,就是数据源和数据订阅者之间的关系,以及设计思路,但是这个需要在说完服务端的处理后,再说这个方面,便于理解,所以这个DataSource和DataSubscriber的问题我们标记为Q4

在前面的4.2的最后,我们提到了ImagePipeline,说到这个,就先说说这个东西是用来做什么的

套用一下Facebook官方的说法

(中文说明 :http://fresco-cn.org/docs/intro-image-pipeline.html#_)

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

大致流程如下:

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

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

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

Fresco 源码分析(三) Fresco服务端处理(1) ImagePipeline为何物

上图中,disk cache实际包含了未解码的内存缓存在内,统一在一起只是为了逻辑稍微清楚一些。

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

(虽然我将Fresco分为前台和后台,其实就是UI层和业务逻辑层的关系,但是前面已经将这个说法说了很久,我也就沿用这个说法了)

Facebook官方中已经说明,ImagePipeline负责完成加载图像,并且将结果反馈(以回调或者说观察者的方式)出来,那么这个我们就认为是后台的处理吧.

言归正传,继续我们的源码分析.

在上篇中,我们分析到了PipelineDraweeControllerBuilder的getDataSourceForRequest方法,我们先来回顾一下:

*** PipelineDraweeControllerBuilder.getDataSourceForRequest() 源码***

从以下的逻辑可以看出,区分了是否只是用于bitmapCacheOnly,按照我们分析的一步一步,在AbstractDraweeControllerBuilder.getDataSourceSupplierForRequest一个参数的构造方法中,看到这个请求的参数是false,所以我们关注的重点来了,就是ImagePipeline.fetchDecodedImage()方法

  @Override
protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
ImageRequest imageRequest,
Object callerContext,
boolean bitmapCacheOnly) {
if (bitmapCacheOnly) {
return mImagePipeline.fetchImageFromBitmapCache(imageRequest, callerContext);
} else {
return mImagePipeline.fetchDecodedImage(imageRequest, callerContext);
}
}

4.3.1 ImagePipeline的处理

直接按照调用的先后顺序来看这个处理的过程吧,在上面已经提到ImagePipeline.fetchDecodedImage()方法,从这部分源码着手,

*** ImagePipeline.fetchDecodedImage() 源码 ***

从方法的注释以及方法名字得知其实提交了一个用于解码的图片的请求,并且返回这个数据源,

按照广度分析,先获取到一个用于解码请求的序列(标记为分支1),然后再提交这个请求(标记为分支2).

 /**
* Submits a request for execution and returns a DataSource representing the pending decoded
* image(s).
*
* <p>The returned DataSource must be closed once the client has finished with it.
* @param imageRequest the request to submit
* @return a DataSource representing the pending decoded image(s)
*/
public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
ImageRequest imageRequest,
Object callerContext) {
try {
Producer<CloseableReference<CloseableImage>> producerSequence =
mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
return submitFetchRequest(
producerSequence,
imageRequest,
ImageRequest.RequestLevel.FULL_FETCH,
callerContext);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}

下面再从深度来查看其处理的逻辑

4.3.1.1 ImagePipeline.fetchDecodedImage() 源码分支1的处理

*** ProducerSequenceFactory.getDecodedImageProducerSequence() 源码 ***

返回一个可用于解码图片的请求的序列

先获取一个基本的解码请求的序列,如果imageRequest.getPostprocessor()不是null的话,基于基本请求序列,再获取到一个PostprocessorSequence返回.

  /**
* Returns a sequence that can be used for a request for a decoded image.
*
* @param imageRequest the request that will be submitted
* @return the sequence that should be used to process the request
*/
public Producer<CloseableReference<CloseableImage>> getDecodedImageProducerSequence(
ImageRequest imageRequest) {
Producer<CloseableReference<CloseableImage>> pipelineSequence =
getBasicDecodedImageSequence(imageRequest);
if (imageRequest.getPostprocessor() != null) {
return getPostprocessorSequence(pipelineSequence);
} else {
return pipelineSequence;
}
}

ImageRequest的Postprocessor是个什么呢?在说这个之前,还要先说一下ImageRequest,翻看ImageRequest的源码得知,ImageRequest是个javabean, 查看Facebook的类的注释: Immutable object encapsulating everything pipeline has to know about requested image to proceed.可以理解为ImagePipeline中需要获取或者需要得知一切请求图片的信息,包括是否用后处理器处理(后处理这个东西我没仔细看,就不班门弄斧了......)

我们就看一般的处理情况即可,就是获取基本的解码请求序列

*** ProducerSequenceFactory.getBasicDecodedImageSequence() 源码 ***

看了这么多,终于看到我们想看到的逻辑了,这里在获取图片时,根据判断请求的uri,网络,本地视频,本地图片,本地assets,数据库等等,生成对应的ImageRequestSequence.

 private Producer<CloseableReference<CloseableImage>> 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);
}
}

那么我们要以一个为例,最复杂的就是网络请求这块,涉及的知识点比较多,就以这个为例吧

*** ProducerSequenceFactory.getNetworkFetchSequence() 源码 ***

根据方法的注释得知,这个可以认为是上面引用的Fresco在提到ImagePipeline的特点

  1. 检查内存缓存,如有,返回

  2. 后台线程开始后续工作

  3. 检查是否在未解码内存缓存中。如有,解码,变换,返回,然后缓存到内存缓存中。

  4. 检查是否在文件缓存中,如果有,变换,返回。缓存到未解码缓存和内存缓存中。

  5. 从网络或者本地加载。加载完成后,解码,变换,返回。存到各个缓存中。

     /**
    * 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<CloseableReference<CloseableImage>> getNetworkFetchSequence() {
    if (mNetworkFetchSequence == null) {
    mNetworkFetchSequence =
    newBitmapCacheGetToDecodeSequence(getCommonNetworkFetchToEncodedMemorySequence());
    }
    return mNetworkFetchSequence;
    }

还是按照我们的说法,从第一次请求开始说起,那么这时mNetworkFetchSequence是未初始化的,那么久需要初始化,即 newBitmapCacheGetToDecodeSequence(getCommonNetworkFetchToEncodedMemorySequence());

我们将getCommonNetworkFetchToEncodedMemorySequence()标记为分支1, newBitmapCacheGetToDecodeSequence标记为分支2

先广度后深度:

广度方向:

分支2:

*** ProducerSequenceFactory.newBitmapCacheGetToDecodeSequence() 源码 ***

根据传递而来的producer,然后生成一个解码的生产者,然后再讲这个生产者作为参数传递给newBitmapCacheGetToBitmapCacheSequence()方法

 /**
* Same as {@code newBitmapCacheGetToBitmapCacheSequence} but with an extra DecodeProducer.
* @param nextProducer next producer in the sequence after decode
* @return bitmap cache get to decode sequence
*/
private Producer<CloseableReference<CloseableImage>> newBitmapCacheGetToDecodeSequence(
Producer<EncodedImage> nextProducer) {
DecodeProducer decodeProducer = mProducerFactory.newDecodeProducer(nextProducer);
return newBitmapCacheGetToBitmapCacheSequence(decodeProducer);
}

这里说到将前一个生产者作为参数传递给下一个生产者,这是为了什么呢?

这个其实就实现了业务的隔离,在上面已经提到了Fresco获取数据的大体流程,内存,本地,网络,如果让我们来写这个过程,可能都会在一个类的核心方法中,将这些逻辑实现,但是Fresco是一个框架,而且是一个很好的框架,实现了这些具体业务之间的隔离,而且是面向的接口编程,从这个newBitmapCacheGetToDecodeSequence()方法来看,是先生成的网络获取的队列,然后传递给解码,解码传递给BitmapCache,这个是个我们获取图片反向的流程,其实就是实现了先从BitmapCache中获取数据,如果数据存在,便不会再次调用里面封装的producer,即decodeProducer,这个便是服务端niubility之一的地方,日常编程中我们可以借鉴这一点哦,个人认为是包装设计模式的典型使用的场所~

这个只是简单的提一下,因为接下来的分析和这个有很大的关系

分支2暂且分析到此,因为广度再次进行,就变成深度了...

分支2遗留的分析标记为Q5

分支1的分析:

*** ProducerSequenceFactory.getCommonNetworkFetchToEncodedMemorySequence() 源码 ***

通用的网络到编码内存的图片获取方式,按照第一次请求

先生成一个网络获取的producer,然后传递给编码内存的producer,然后再传递给mCommonNetworkFetchToEncodedMemorySequence,如果允许网络图片的重新设置大小和旋转,那么再把这个producder传递给用于处理旋转和重新设置大小的producer

  /**
* multiplex -> encoded cache -> disk cache -> (webp transcode) -> network fetch.
*/
private synchronized Producer<EncodedImage> getCommonNetworkFetchToEncodedMemorySequence() {
if (mCommonNetworkFetchToEncodedMemorySequence == null) {
Producer<CloseableReference<PooledByteBuffer>> nextProducer =
newEncodedCacheMultiplexToTranscodeSequence(
mProducerFactory.newNetworkFetchProducer(mNetworkFetcher));
mCommonNetworkFetchToEncodedMemorySequence =
ProducerFactory.newAddImageTransformMetaDataProducer(nextProducer); if (mResizeAndRotateEnabledForNetwork) {
mCommonNetworkFetchToEncodedMemorySequence =
mProducerFactory.newResizeAndRotateProducer(
mCommonNetworkFetchToEncodedMemorySequence);
}
}
return mCommonNetworkFetchToEncodedMemorySequence;
}

本节就先分析到这里,下一章节会分析服务端核心的处理流程之一:Producer具体实现的内容

安卓源码分析群: Android源码分析QQ1群号:164812238

上一篇:motan源码分析四:客户端调用服务


下一篇:Fresco 源码分析(二) Fresco客户端与服务端交互(2) Fresco.initializeDrawee()分析 续