史上最細Glide原始碼解讀(一) : 主流程分析
theme: cyanosis highlight: androidstudio
前言
本篇作為史上最細Glide原始碼解讀系列第一篇 , 主要對主流程進行分析
以後還會有若干篇對Glide 中 運用的設計模式 / 執行緒池 /圖片優化/ 解碼/轉碼/快取 等細節的文章釋出
使用
假設呼叫以下程式碼進行圖片載入
kotlin
Glide.with(activity).load("https/http開頭的圖片連結").into(imageView)
先來一張思維導圖
流程分析開始 , 前排請繫好安全帶
with 方法
with 方法呼叫如下 ```kotlin public static RequestManager with(@NonNull Activity activity) { return getRetriever(activity).get(activity); }
private static RequestManagerRetriever getRetriever(@Nullable Context context) { return Glide.get(context).getRequestManagerRetriever(); }
public RequestManager get(@NonNull Activity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm, /parentHint=/ null, isActivityVisible(activity)); } }
``` with 方法主要執行以下幾歩
-
- 初始化Glide
-
- 呼叫RequestManagerRetriever.get 構建RequestManager 並返回 RequestManager
-
- 將request 和 Activity 生命週期繫結
初始化Glide
Glide 全域性單例 ,建構函式如下 ```kotlin Glide( //... 引數省略//) { this.engine = engine; this.bitmapPool = bitmapPool; this.arrayPool = arrayPool; this.memoryCache = memoryCache; this.requestManagerRetriever = requestManagerRetriever; this.connectivityMonitorFactory = connectivityMonitorFactory; this.defaultRequestOptionsFactory = defaultRequestOptionsFactory;
final Resources resources = context.getResources();
registry = new Registry();
//拼接到 model loader表
registry
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
//拼接到 decoder 表
registry
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
InputStream.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
//... 省略大部分registry.append(xxx)程式碼
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
``` registry 是一張登錄檔 , glide 所有操作需要的類都在這張登錄檔裡面 , 建議背熟此表哈哈哈
比如 append(String.class, InputStream.class, new StringLoader.StreamFactory()) , 輸入為url String , 輸出為InputStream 資料 , 操作類為StringLoader , StringLoader 作用將圖片的 # load(url)方法輸入的url 字串轉換為 InputStream
append(Registry.BUCKET_BITMAP_DRAWABLE, InputStream.class, BitmapDrawable.class,new BitmapDrawableDecoder ) , 輸入為 InputStream , 輸出為BitmapDrawable , BitmapDrawableDecoder 作用將InputStream 解碼轉換為 BitmapDrawable
registry 登錄檔有很多 , 作用在註釋中
```kotlin
//model 載入表 , 將model 轉換為可供解碼的資料如Stream
private final ModelLoaderRegistry modelLoaderRegistry;
//編碼表 , 將InputStream / ByteBuffer 編碼成File , 快取在磁碟中 private final EncoderRegistry encoderRegistry;
//資源解碼錶 , 將Stream等資料解碼為可供ImageView顯示的GifDrawable / Bitmap / BitmapDrawable private final ResourceDecoderRegistry decoderRegistry;
//資源編碼表 , 將GifDrawable / Bitmap / BitmapDrawable 編碼成File , 快取在磁碟中 private final ResourceEncoderRegistry resourceEncoderRegistry;
//流回卷表, 用於將流回退重讀 private final DataRewinderRegistry dataRewinderRegistry;
//轉碼錶 , 用於將Bitmap / GifDrawable 轉碼為 BitmapDrawable / byte[].class private final TranscoderRegistry transcoderRegistry;
//圖片頭解析表 , 用於解析圖片頭 private final ImageHeaderParserRegistry imageHeaderParserRegistry; ``` 上面這幾張表 , glide 大部分功能都會用到 , 非常複雜 , 以後會出一篇文章詳細解答 , 繼續關注主流程
RequestManagerRetriever#get
RequestManagerRetriever.get 方法中將 request 和 Activity 生命週期繫結 , 通過Fragment .commitAllowingStateLoss 方法 , 使用透明的Fragment 監聽Activity 生命週期 , onStart 啟動 圖片載入請求 , onStop暫停圖片載入請求 , Fragment # onStop 最終會呼叫到 requestTracker#pauseRequests
kotlin
public void pauseRequests() {
isPaused = true;
for (Request request : Util.getSnapshot(requests)) {
if (request.isRunning()) {
//status == Status.RUNNING || status == Status.WAITING_FOR_SIZE
request.pause();
pendingRequests.add(request);
}
}
}
如果正在請求 , request#pause 方法會把此次請求的資料clear掉 , 但是並不會清理已快取在磁碟中的資料
kotlin
// SingleRequest#clear
public void clear() {
Resource<R> toRelease = null;
synchronized (requestLock) {
if (status == Status.CLEARED) {
return;
}
cancel();
if (resource != null) {
toRelease = resource;
resource = null;
}
if (canNotifyCleared()) {
target.onLoadCleared(getPlaceholderDrawable());
}
status = Status.CLEARED;
}
if (toRelease != null) {
engine.release(toRelease);
}
}
總結下with 方法 , 初始化 glide 把所需的類放在表中儲存起來 , 監聽Activity 生命週期用於控制開始和取消請求
load 方法
load 方法從RequestManager 開始 ,
```java
//RequestManager#load
public RequestBuilder
public class RequestBuilder構建一個RequestBuilder 並返回 , **RequestBuilder 的泛型TranscodeType 為 Drawable**
RequestBuilder 構造方法內會應用預設的請求配置和請求監聽
java
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class
``` requestManager 有setDefaultRequestOptions , addDefaultRequestListener 可以設定請求配置和請求監聽 總結下load 方法 , 構建RequestBuilder 然後應用請求配置和監聽
into 方法
經過with , load 兩個方法 , 圖片請求相關的配置都已經處理了 , 接下來就是構建 Request 去請求圖片的資源了
RequestBuilder#into 方法
java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
根據ImageView 的 ScaleType 設定圖片 Transform , 因為transcodeClass 為 Drawable 所以glideContext.buildImageViewTarget返回的為 DrawableImageViewTarget , 然後傳入圖片處理成功之後的在主執行緒執行的執行緒池
繼續更進into方法 , 會構建出 Request , 然後將 Request 放入到 RequestManager 變數targetTracker中 , 接著呼叫 request.begin() 方法 , RequestManager 中變數儲存的 Request 使用的是WeakHashMap , 防止 Request 持有ImageView 引用造成記憶體洩漏
繼續檢視Request#begin 方法 , 會先獲取到圖片的Width 和 Height , 在圖片的Width 和 Height有效的情況 , 會呼叫 engine.load 方法 , engine是Glide 中的變數 , 全域性單例
```java
public void begin() {
synchronized (requestLock) {
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
}
}
public void onSizeReady(int width, int height) {
synchronized (requestLock) {
status = Status.RUNNING;
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
// ... 省略相關配置//
this,
callbackExecutor);
}
}
```
engine#load 方法 , 主要分兩歩
-
- 先從記憶體快取中判斷是否有快取 , 如果有則從記憶體快取中獲取直接返回
-
- 記憶體快取中沒有快取 , 就構建engineJob 和 decodeJob , decodeJob 負責用於去下載和解碼源資料 , engineJob 負責開始/取消decodeJob , 圖片載入相關的回撥
預設情況下Glide啟用RESOURCE 和 DATA 的 快取 , engineJob 會將decodeJob 放入 diskCacheExecutor執行緒池中執行 , DecodeJob #run 會執行runWrapped , runWrapped 才是關鍵方法
DecodeJob #runWrapped
java
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
}
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
//遞迴獲取Stage
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
}
}
runWrapped 分三步
-
- 先從RESOURCE_CACHE 中去找 , 這個快取拿到的圖片已經解碼過可以直接給ImageView使用, 對應的是 ResourceCacheGenerator這個類
-
- 如果 RESOURCE_CACHE 快取中沒有 , 則從 DATA_CACHE 中查詢 , DATA_CACHE 快取中快取的是未經解碼的源資料 , 需要解碼才能給ImageView使用 , 對應的是DataCacheGenerator這個類
-
- 如果RESOURCE_CACHE 和 DATA_CACHE 中都沒有找到 , 則從源(伺服器或者本地)去找到圖片資料 , 對應的是 SourceGenerator這個類
因為首次請求沒有快取所以會呼叫 SourceGenerator #startNext , 此方法會從 ModelLoader 登錄檔中獲取 ModelLoader 載入Model 對應的源資料
SourceGenerator #startNext
startNext 方法作用是拿到圖片的源資料 , 如果可以快取就把源資料快取 ```java public boolean startNext() { loadData = null; boolean started = false; while (!started && hasNextModelLoader()) { loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true; startNextLoad(loadData); } } return started; }
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List
private static final String DATA_SCHEME_IMAGE = "data:image";
@Override
public boolean handles(@NonNull Model model) {
return model.toString().startsWith(DATA_SCHEME_IMAGE);
}
}
ModelLoaderRegistry#getModelLoaders( A model) 中會根據 **ModelLoader的 handles** 方法來過濾
java
public List
}
return filteredLoaders;
}
StringLoader 又會丟給StringLoader 中的 uriLoader 去處理 ,** StringLoader #buildLoadData 會根據model 進行篩選 **, 如果是 '/' 開頭的本地圖片會返回 null , 因為model 為 http/https 開頭 ,詳細程式碼如下
java
public LoadData buildLoadData(
@NonNull String model, int width, int height, @NonNull Options options) {
Uri uri = parseUri(model);
//非http/https 的返回null
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
//model 為http/https 開頭
private static Uri parseUri(String model) {
Uri uri;
if (TextUtils.isEmpty(model)) {
return null;
} else if (model.charAt(0) == '/') {
uri = toFileUri(model);
} else {
uri = Uri.parse(model);
String scheme = uri.getScheme();
if (scheme == null) {
uri = toFileUri(model);
}
}
return uri;
}
經過重重的篩選就只有**StreamFactory#build() 的 StringLoader** 可以供 model 使用
java
public ModelLoader這樣就可以去通過StringLoader載入圖片了 ? 還沒完 , 還會通過 Uri.class 和 InputStream.class 去找到與之匹配的modelLoader

會找到HttpUriLoader , HttpUriLoader 又會通過 **GlideUrl.class, InputStream.class** 找到與之匹配的**HttpGlideUrlLoader**, 是不是有點俄羅斯套娃的感覺 , 一層套一層
java
public class HttpUriLoader implements ModelLoader
public static class Factory implements ModelLoaderFactory<Uri, InputStream> {
@NonNull
@Override
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
}
}
}

HttpGlideUrlLoader 應該沒有套了吧 ? 繼續點進HttpGlideUrlLoader 檢視
java
public class HttpGlideUrlLoader implements ModelLoader
/** The default factory for {@link HttpGlideUrlLoader}s. */
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
@Override
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpGlideUrlLoader(modelCache);
}
}
} ``` 果不其然 , HttpGlideUrlLoader 的Factory#build 直接返回HttpGlideUrlLoader , 停止套娃了 經過重重篩選 ,HttpGlideUrlLoader .Factory 生成的 HttpGlideUrlLoader , 才是model 需要的loader , HttpGlideUrlLoader 會通過buildLoadData 方法生成 LoadData , 去伺服器下載圖片的原始檔就是通過LoadData完成 接下來看看HttpGlideUrlLoader #buildLoadData 方法
HttpGlideUrlLoader #buildLoadData
java
public LoadData<InputStream> buildLoadData(
@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
會new 一個 LoadData , fetcher 為 HttpUrlFetcher
回到 SourceGenerator#startNextLoad 中
java
private void startNextLoad(final LoadData<?> toStart) {
loadData.fetcher.loadData(
// ...省略回撥 //);
}
繼續更進HttpUrlFetcher # loadData
```java
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
}
}
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map
interface HttpUrlConnectionFactory { HttpURLConnection build(URL url) throws IOException; }
private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {
@Override
public HttpURLConnection build(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
}
```
歷經百般阻撓 , 終於看到 從伺服器下載圖片相關的程式碼 , glide 通過系統提供的 HttpURLConnection去伺服器下載圖片的原始檔
圖片源資料下載好之後 , 會通過DataCallback 回撥到SourceGenerator#onDataReadyInternal中
SourceGenerator#onDataReadyInternal
處理圖片的源資料
java
void onDataReadyInternal(LoadData<?> loadData, Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
cb.reschedule();
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
data為InputStream , 預設快取是支援DATA_CACHE , 所以會走進if 條件內, dataToCache = data , cb 為 DecodeJob, 最終會通知EngineJob 重新將 DecodeJob丟進執行緒池中
java
//SourceGenerator#onDataReadyInternal
void onDataReadyInternal(LoadData<?> loadData, Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
cb.reschedule();
}
}
//DecodeJob#reschedule
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
//EngineJob#reschedule
public void reschedule(DecodeJob<?> job) {
// Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
// up.
getActiveSourceExecutor().execute(job);
}
再次回到DecodeJob#runWrapped
此時 runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; 會直接執行runGenerators() 方法 , currentGenerator 還是原來的SourceGenerator , 所以會再執行SourceGenerator#startNext 方法 ```java class SourceGenerator implements DataFetcherGenerator, DataFetcherGenerator.FetcherReadyCallback {
private DataCacheGenerator sourceCacheGenerator;
private Object dataToCache;
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
return started;
}
private void cacheData(Object dataToCache) {
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
}
此時 dataToCache 不為 null , 會通過**DiskCache 將源資料快取在檔案** 中 , encoder為 StreamEncoder , 將圖片快取在檔案中然後會構建出 DataCacheGenerator 接著執行**DataCacheGenerator#startNext** 方法
java
class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
} ``` 此時stage = getNextStage(stage) = FINISHED , 會呼叫 notifyFailed , 所以當解碼之後resource == null 情況下, 會呼叫 notifyFailed 通知圖片載入失敗 , 並不會死迴圈執行
繼續分析第二步 , Stream 解碼成 Resource
最終會呼叫 decodeFromData() -> decodeFromFetcher() 方法 , 在這個方法中會通過 decodeHelper.getLoadPath((Class) data.getClass())去拿到 LoadPath, 看來又是通過登錄檔中去找的
java
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
先看看 dataClass, resourceClass, transcodeClass 是什麼?
- dataClass是從DataCacheGenerator 通過 FileLoader 回撥過來的 InputStream , 所以 dataClass = InputStream.class
- resourceClass 從 BaseRequestOptions 設定, 如果不設定預設為 Object.class
- transcodeClass 是 class RequestBuilder
的泛型TranscodeType , 由as方法指定 , 此處為Drawable.class
LoadPath 功能由 List
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
//1. 根據dataClass, resourceClass找到所有registeredResourceClasses
List<Class<TResource>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
//2. 根據registeredResourceClass找到所有registeredTranscodeClasses
List<Class<Transcode>> registeredTranscodeClasses =
transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
//3. 根據registeredResourceClass找到所有decoders
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);
//4. 根據registeredTranscodeClass找到所有transcoder
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
//5. 構建DecodePath
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(
dataClass,
registeredResourceClass,
registeredTranscodeClass,
decoders,
transcoder,
throwableListPool);
decodePaths.add(path);
}
}
return decodePaths;
}
``` 我把上面程式碼分成5小步 , 下面詳細看下這5歩
- 根據dataClass, resourceClass找到所有registeredResourceClasses
可以就只有看到Bitmap.class 滿足條件 , 因為GifDrawable.class 的dataclass為ByteBuffer 不是InputStream的子類 , 所以registeredResourceClasses只有一個數據 Bitmap.class
- 根據registeredResourceClass找到所有registeredTranscodeClasses
通過getTranscodeClasses方法 去獲取registeredTranscodeClasses
```java
List
public synchronized
-
根據registeredResourceClass找到所有decoders
java List<ResourceDecoder<Data, TResource>> decoders = decoderRegistry.getDecoders(dataClass, registeredResourceClass);
dataClass為InputStream.class , registeredResourceClass 為Bitmap.class 符合條件的就只有streamBitmapDecoder , streamBitmapDecoder又會根據 Android 版本構建 , 本文以StreamBitmapDecoder來分析java ResourceDecoder<InputStream, Bitmap> streamBitmapDecoder; if (isImageDecoderEnabledForBitmaps && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { streamBitmapDecoder = new InputStreamBitmapImageDecoderResourceDecoder(); byteBufferBitmapDecoder = new ByteBufferBitmapImageDecoderResourceDecoder(); } else { byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler); streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool); }
-
根據registeredTranscodeClass找到所有transcoder
java ResourceTranscoder<TResource, Transcode> transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
轉碼錶中根據registeredResourceClass =Bitmap.class 和 registeredTranscodeClass =Drawable.class 去獲取 transcoder符合條件就只有 BitmapDrawableTranscoder了
總結
decodePaths集合中 只有一個DecodePath例項物件 , decoders 只有一個streamBitmapDecoder 例項物件 , transcoder為 BitmapDrawableTranscoder
java
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(
dataClass,
registeredResourceClass,
registeredTranscodeClass,
decoders,
transcoder,
throwableListPool);
decodePaths.add(path);
從 InputStram ->Bitmap -> Drawable這條先找匹配的解碼器後找匹配的轉碼器的路徑 的LoadPath 就分析完了 ,解碼和轉碼的細節以後再抽一篇文章分析 , 繼續關注主流程
繼續回到DecodeJob#decodeFromRetrievedData方法
```java
private void decodeFromRetrievedData() {
Resource
異常情況就先不去分析了 , 假設解碼和轉碼成功下 , 此時Resource<R> 不為空了, 並且**泛型R** 和 LoadPath<Data, ?, R> 的泛型R 一樣**為BitmapDrawable**, 繼續進入notifyEncodeAndRelease
java
private void notifyEncodeAndRelease(Resource
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
//顯示圖片
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
//快取
deferredEncodeManager.encode(diskCacheProvider, options);
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
} ``` 以上程式碼分兩步
- 回撥ImageView 顯示圖片
圖片顯示回撥鏈 , DecodeJob#notifyComplete() -> EngineJob#onResourceReady()-> ....->主執行緒池執行SingleRequest#onResourceReady 開始啟動DecodeJob 之前 , Engine#waitForExistingOrStartNewJob 中給EngineJob新增Callback , cb 為SingleRequest , callbackExecutor為在主執行緒執行的執行緒池 ```java
private
synchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) { cbs.add(cb, callbackExecutor); }
void add(ResourceCallback cb, Executor executor) {
callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));
}
轉碼成功後 , EngineJob中處理回撥
java
void notifyCallbacksOfResult() {
for (final ResourceCallbackAndExecutor entry : callbacksAndExecutors) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
private class CallResourceReady implements Runnable {
private final ResourceCallback cb;
@Override
public void run() {
callCallbackOnResourceReady(cb);
}
}
void callCallbackOnResourceReady(ResourceCallback cb) { cb.onResourceReady(engineResource, dataSource); } ```
顯示圖片
SingleRequest最終會呼叫DrawableImageViewTarget#setResource
java
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
- 把 Resource
快取在磁碟中
onResourceDecoded 會在解碼和轉碼成功後回撥 , 預設 isResourceCacheable = true , 支援ResourceCache ,
呼叫deferredEncodeManager.init() 給 toEncode 賦值
java
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
if //省略程式碼
(diskCacheStrategy.isResourceCacheable(
isFromAlternateCacheKey, dataSource, encodeStrategy)) {
// 支援ResourceCache 給toEncode 賦值
deferredEncodeManager.init(key, encoder, lockedResult);
}
}
快取圖片
回撥圖片顯示之後 , 判斷如果toEncode !=null , 表示有圖片要快取, 則呼叫 encode 使用DiskCache 把Resourcejava
void encode(DiskCacheProvider diskCacheProvider, Options options) {
try {
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
}
}
總結
通過請求引數構建圖片Request , 然後把Request放入到RequestManager 管理 , 請求的生命週期控制通過Fragment 實現 圖片的寬高準備好之後呼叫Request#begin方法啟動Engine開始請求
Engine 負責構建出EngineJob 和DecodeJob ,並且負責連線EngineJob 和DecodeJob
EngineJob負責管理(開始/取消) DecodeJob , 以及將圖片請求的結果回撥給SingleRequest/ImageView
DecodeJob負責獲取圖片源資料/解碼/轉碼 , 主要功能由內部變數 decodeHelper 實現
再次貼出文章開始放的圖