4.3 服務端的處理#
備注: 因為是分析,而不是設計,所以很多知識我們類似於插敘的方式敘述,就是用到了哪個知識點,我們再提及相關的知識點,如果分析到了最后,我想想是不是應該將這個架構按照設計的方式,重新梳理一下(套用一句話,現在安卓的應用其實很多都像是快餐,至今面試了應該有40多位的安卓程序員,知道框架的很多,會用的也很多,會總結和整理的卻沒有幾個,想到安卓程序員薪資超過20K的並不多,個人感覺因為很多都只是會簡單的UI層的處理,稍微設計到業務邏輯層,即設計一套機制的時候,便卡頓了,這應該算是瓶頸吧)
插敘: 其實在客戶端與服務端的交互中,還設計到了一個知識點,就是數據源和數據訂閱者之間的關系,以及設計思路,但是這個需要在說完服務端的處理后,再說這個方面,便於理解,所以這個DataSource和DataSubscriber的問題我們標記為Q4
在前面的4.2的最后,我們提到了ImagePipeline,說到這個,就先說說這個東西是用來做什么的
套用一下Facebook官方的說法
(中文說明 :http://fresco-cn.org/docs/intro-image-pipeline.html#_)
Image pipeline 負責完成加載圖像,變成Android設備可呈現的形式所要做的每個事情。
大致流程如下:
- 檢查內存緩存,如有,返回
- 后台線程開始后續工作
- 檢查是否在未解碼內存緩存中。如有,解碼,變換,返回,然后緩存到內存緩存中。
- 檢查是否在文件緩存中,如果有,變換,返回。緩存到未解碼緩存和內存緩存中。
- 從網絡或者本地加載。加載完成后,解碼,變換,返回。存到各個緩存中。
既然本身就是一個圖片加載組件,那么一圖勝千言。
既然本身就是一個圖片加載組件,那么一圖勝千言。

上圖中,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的特點
-
檢查內存緩存,如有,返回
-
后台線程開始后續工作
-
檢查是否在未解碼內存緩存中。如有,解碼,變換,返回,然后緩存到內存緩存中。
-
檢查是否在文件緩存中,如果有,變換,返回。緩存到未解碼緩存和內存緩存中。
-
從網絡或者本地加載。加載完成后,解碼,變換,返回。存到各個緩存中。
/** * 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
