史上最細Glide原始碼解讀(一) : 主流程分析

語言: CN / TW / HK

theme: cyanosis highlight: androidstudio


前言

本篇作為史上最細Glide原始碼解讀系列第一篇 , 主要對主流程進行分析
以後還會有若干篇對Glide 中 運用的設計模式 / 執行緒池 /圖片優化/ 解碼/轉碼/快取 等細節的文章釋出

使用

假設呼叫以下程式碼進行圖片載入 kotlin Glide.with(activity).load("https/http開頭的圖片連結").into(imageView) 先來一張思維導圖 glide 思維圖.png 流程分析開始 , 前排請繫好安全帶

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 方法主要執行以下幾歩

    1. 初始化Glide
    1. 呼叫RequestManagerRetriever.get 構建RequestManager 並返回 RequestManager
    1. 將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 load(@Nullable String string) { return asDrawable().load(string); } public RequestBuilder as( @NonNull Class resourceClass) { return new RequestBuilder<>(glide, this, resourceClass, context); }

public class RequestBuilder extends BaseRequestOptions> implements Cloneable, ModelTypes> { } 構建一個RequestBuilder 並返回 , **RequestBuilder 的泛型TranscodeType 為 Drawable** RequestBuilder 構造方法內會應用預設的請求配置和請求監聽java protected RequestBuilder( @NonNull Glide glide, RequestManager requestManager, Class transcodeClass, Context context) { this.glide = glide; this.requestManager = requestManager; this.transcodeClass = transcodeClass; this.context = context; this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass); this.glideContext = glide.getGlideContext(); //應用預設的請求監聽 initRequestListeners(requestManager.getDefaultRequestListeners()); //應用預設的請求配置 apply(requestManager.getDefaultRequestOptions()); }

``` 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 方法 , 主要分兩歩

    1. 先從記憶體快取中判斷是否有快取 , 如果有則從記憶體快取中獲取直接返回
    1. 記憶體快取中沒有快取 , 就構建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 分三步

    1. 先從RESOURCE_CACHE 中去找 , 這個快取拿到的圖片已經解碼過可以直接給ImageView使用, 對應的是 ResourceCacheGenerator這個類
    1. 如果 RESOURCE_CACHE 快取中沒有 , 則從 DATA_CACHE 中查詢 , DATA_CACHE 快取中快取的是未經解碼的源資料 , 需要解碼才能給ImageView使用 , 對應的是DataCacheGenerator這個類
    1. 如果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> modelLoaders = glideContext.getRegistry().getModelLoaders(model); for (int i = 0, size = modelLoaders.size(); i < size; i++) { ModelLoader modelLoader = modelLoaders.get(i); LoadData<?> current = modelLoader.buildLoadData(model, width, height, options); if (current != null) { loadData.add(current); } } } return loadData; } 上面方法程式碼有點長 , glideContext.getRegistry().getModelLoaders(model) 獲取所有能處理此 model的 ModelLoaders , 然後把modelLoader 遍歷構建成 List<LoadData<?>> 返回 **model 為String** , 所以 **ModelLoaders**為在modelLoader 表註冊的 **StringLoader 和 DataUrlLoader** ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/19129ab0cad847588ac2f464d45789c3~tplv-k3u1fbpfcp-zoom-1.image) 但因為傳入的model 為http / https 開頭 , DataUrlLoader會被過濾掉 , 就只剩下**StringLoader **了java public final class DataUrlLoader implements ModelLoader {

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> getModelLoaders(@NonNull A model) { for (int i = 0; i < size; i++) { ModelLoader loader = modelLoaders.get(i); //loader.handles 返回true 才會新增到 modelLoaders中 if (loader.handles(model)) { filteredLoaders.add(loader); }
} 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 build(@NonNull MultiModelLoaderFactory multiFactory) { return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class)); } 這樣就可以去通過StringLoader載入圖片了 ? 還沒完 , 還會通過 Uri.class 和 InputStream.class 去找到與之匹配的modelLoader ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/652689c806e74cdfb5a1d8f0313a660c~tplv-k3u1fbpfcp-zoom-1.image) 會找到HttpUriLoader , HttpUriLoader 又會通過 **GlideUrl.class, InputStream.class** 找到與之匹配的**HttpGlideUrlLoader**, 是不是有點俄羅斯套娃的感覺 , 一層套一層java public class HttpUriLoader implements ModelLoader { private static final Set SCHEMES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));

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));
    }
}

} ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d6b02c3acfbd4a85a0334f5a99bf8f14~tplv-k3u1fbpfcp-zoom-1.image) 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 headers) throws IOException { //...省略程式碼 // urlConnection = connectionFactory.build(url); for (Map.Entry headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.connect(); stream = urlConnection.getInputStream(); //取消直接返回null if (isCancelled) { return null; } return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); }

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 {

@Override
public boolean startNext() {
    while (modelLoaders == null || !hasNextModelLoader()) {

        cacheFile = helper.getDiskCache().get(originalKey);

        while (!started && hasNextModelLoader()) {
            ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);

            if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
                started = true;
                loadData.fetcher.loadData(helper.getPriority(), this);
            }
        }
        return started;
    }

} DataCacheGenerator#startNext 和 SourceGenerator 也是差不多的思路 , 先拿到剛剛快取在磁碟中的cacheFile , 然後找到源File對應的 FileLoader , 通過FileLoader 的loadData.fetcher去**拿到快取在File中圖片Stream資料** , 這裡貼點關鍵程式碼java public class FileLoader implements ModelLoader { private static final String TAG = "FileLoader";

@Override
public LoadData<Data> buildLoadData(
    @NonNull File model, int width, int height, @NonNull Options options) {
    return new LoadData<>(new ObjectKey(model), new FileFetcher<>(model, fileOpener));
}

private static final class FileFetcher<Data> implements DataFetcher<Data> {

    @Override
    public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
        try {
            data = opener.open(file);
        } 
        callback.onDataReady(data);
    }
}

public static class StreamFactory extends Factory<InputStream> {
    public StreamFactory() {
        super(
            new FileOpener<InputStream>() {
                @Override
                public InputStream open(File file) throws FileNotFoundException {
                    return new FileInputStream(file);
                }
            });
    }
}

}

``` 拿到了圖片的 FileInputStream資料之後 , 會回撥到DecodeJob#onDataFetcherReady 方法中

DecodeJob#onDataFetcherReady

這一步之前已經將圖片的源資料快取在DATA_CACHE 中了 , 現在是將源資料從DATA_CACHE 中拿出來並且轉換為Stream了 這裡判斷了下是不是和 runGenerators 的執行緒一致 , 不一致則重新排程下, 目的是讓其執行在對應的執行緒池中 ```java private void runGenerators() { currentThread = Thread.currentThread(); }
@Override public void onDataFetcherReady( Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {

if (Thread.currentThread() != currentThread) {
    runReason = RunReason.DECODE_DATA;
    callback.reschedule(this);
} else {
    try {
        decodeFromRetrievedData();
    } finally {
        GlideTrace.endSection();
    }
}

} ``` 最終都會調到decodeFromRetrievedData 方法

DecodeJob#decodeFromRetrievedData

主要有3步

    1. 將Stream 解碼成 Resource
    1. 將 Resource 快取在 RESOURCE_CACHE 中
    1. 如果resource == null , 會再次呼叫 runGenerators

基於第三步 , 先提出個問題

如果resource == null , 執行runGenerators 又會呼叫 DataCacheGenerator#startNext 方法 , startNext 方法最終又會調到 decodeFromRetrievedData , ( decodeFromRetrievedData -> DataCacheGenerator#startNext -> 又回撥到 decodeFromRetrievedData)這樣不是死迴圈了 ? java private void decodeFromRetrievedData() { Resource<R> resource = null; try { //1. 解碼 resource = decodeFromData(currentFetcher, currentData, currentDataSource); } if (resource != null) { //2. 回撥將圖片顯示 , 然後快取在 RESOURCE_CACHE 中 notifyEncodeAndRelease(resource, currentDataSource); } else { //3. 再次呼叫 runGenerators runGenerators(); } } 解答第三歩提出死迴圈問題 , 因為在DataCacheGenerator#startNext中有處理 ```java public boolean startNext() { // 通過第三步runGenerators(); //再次呼叫進來 modelLoaders 肯定不為null ,跳過這個迴圈 while (modelLoaders == null || !hasNextModelLoader()) {

}
boolean started = false;
while (!started && hasNextModelLoader()) {
    ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
}
return started;

}

private boolean hasNextModelLoader() { return modelLoaderIndex < modelLoaders.size(); } 在第二個while迴圈中 ,每次迴圈一次 modelLoaderIndex++ , 而hasNextModelLoader() 方法如果modelLoaderIndex>=modelLoaders 會返回false , 直接跳過迴圈 return false 如果 DataCacheGenerator#startNext 返回 false , 再看看 runGeneratorsjava private void runGenerators() {

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 是什麼?

  1. dataClass是從DataCacheGenerator 通過 FileLoader 回撥過來的 InputStream , 所以 dataClass = InputStream.class
  2. resourceClass 從 BaseRequestOptions 設定, 如果不設定預設為 Object.class
  3. transcodeClass 是 class RequestBuilder 的泛型TranscodeType , 由as方法指定 , 此處為Drawable.class

image.pngLoadPath 功能由 List> 實現 , DecodePath 表示 從Data->TResource->Transcode 的一條解碼路徑 , 生成程式碼如下 ```java private List> getDecodePaths( @NonNull Class dataClass, @NonNull Class resourceClass, @NonNull Class transcodeClass) {

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歩

  1. 根據dataClass, resourceClass找到所有registeredResourceClasses

image.png 可以就只有看到Bitmap.class 滿足條件 , 因為GifDrawable.class 的dataclass為ByteBuffer 不是InputStream的子類 , 所以registeredResourceClasses只有一個數據 Bitmap.class

  1. 根據registeredResourceClass找到所有registeredTranscodeClasses

通過getTranscodeClasses方法 去獲取registeredTranscodeClasses ```java List> registeredTranscodeClasses = transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);

public synchronized List> getTranscodeClasses( @NonNull Class resourceClass, @NonNull Class transcodeClass) { List> transcodeClasses = new ArrayList<>(); for (Entry<?, ?> entry : transcoders) { if (entry.handles(resourceClass, transcodeClass)) { transcodeClasses.add(transcodeClass); } } return transcodeClasses; } ``` transcodeClass 為 Drawable.class 所以registeredTranscodeClasses 也和registeredResourceClasses 一樣只有一個數據 Drawable.class

  1. 根據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); }

  2. 根據registeredTranscodeClass找到所有transcoder java ResourceTranscoder<TResource, Transcode> transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass); 轉碼錶中根據registeredResourceClass =Bitmap.class 和 registeredTranscodeClass =Drawable.class 去獲取 transcoder image.png 符合條件就只有 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 = null; try { resource = decodeFromData(currentFetcher, currentData, currentDataSource); } if (resource != null) { notifyEncodeAndRelease(resource, currentDataSource); } }

異常情況就先不去分析了 , 假設解碼和轉碼成功下 , 此時Resource<R> 不為空了, 並且**泛型R** 和 LoadPath<Data, ?, R> 的泛型R 一樣**為BitmapDrawable**, 繼續進入notifyEncodeAndReleasejava private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {

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();

} ``` 以上程式碼分兩步

  1. 回撥ImageView 顯示圖片

圖片顯示回撥鏈 , DecodeJob#notifyComplete() -> EngineJob#onResourceReady()-> ....->主執行緒池執行SingleRequest#onResourceReady 開始啟動DecodeJob 之前 , Engine#waitForExistingOrStartNewJob 中給EngineJob新增Callback , cb 為SingleRequest , callbackExecutor為在主執行緒執行的執行緒池 ```java

private LoadStatus waitForExistingOrStartNewJob( ResourceCallback cb, Executor callbackExecutor, ) { engineJob.addCallback(cb, callbackExecutor); }

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); }

  1. 把 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 把Resource 快取起來 java 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 實現

再次貼出文章開始放的圖 glide 思維圖.png