Glide源碼淺析

前言

之前對Glide的認知一直停留在一行代碼就能夠完成圖片加載,如今就來嘗試探索下這一行代碼下,Glide到底作了些什麼。本文基於Glide4.8.0java

1、基本使用

以加載一張普通的網絡圖片爲例緩存

val jpg = "http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg"
Glide.with(this)
     .load(img)
     .into(iv)
複製代碼

就這麼核心的一行代碼一張圖片就會加載到iv中去了,下面從with開始分析下它的源碼bash

2、with()

with方法提供了不少方法重載,入參都是一些能直接或間接取到Context實例的類型,其基本流程以下網絡

下面根據這個流程來看看源碼app

// Glide.java
public static RequestManager with(@NonNull FragmentActivity activity) {
	return getRetriever(activity).get(activity);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
    if (glide == null) {
        synchronized (Glide.class) {
            if (glide == null) {
              checkAndInitializeGlide(context);
            }
        }
    }
    return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context) {
    initializeGlide(context);
}
private static void initializeGlide(@NonNull Context context) {
    initializeGlide(context, new GlideBuilder());
}
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    RequestManagerRetriever.RequestManagerFactory factory = null;
    builder.setRequestManagerFactory(factory);
    Glide glide = builder.build(applicationContext);
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
}
複製代碼

經過源碼發現getRetriever方法內部其實就是建立了Glide實例,而且調用其getRequestManagerRetriever,先來看看Glide實例的構建過程ide

Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
        sourceExecutor = GlideExecutor.newSourceExecutor();
    }
    if (diskCacheExecutor == null) {
        diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
    if (animationExecutor == null) {
        animationExecutor = GlideExecutor.newAnimationExecutor();
    }
    if (memorySizeCalculator == null) {
        memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }
    if (connectivityMonitorFactory == null) {
        connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }
    if (bitmapPool == null) {
        int size = memorySizeCalculator.getBitmapPoolSize();
        if (size > 0) {
            bitmapPool = new LruBitmapPool(size);
        } else {
            bitmapPool = new BitmapPoolAdapter();
        }
    }
    if (arrayPool == null) {
        arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }
    if (memoryCache == null) {
        memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
    if (diskCacheFactory == null) {
        diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
    if (engine == null) {
        engine =
                new Engine(
                        memoryCache,
                        diskCacheFactory,
                        diskCacheExecutor,
                        sourceExecutor,
                        GlideExecutor.newUnlimitedSourceExecutor(),
                        GlideExecutor.newAnimationExecutor(),
                        isActiveResourceRetentionAllowed);
    }
    RequestManagerRetriever requestManagerRetriever =
            new RequestManagerRetriever(requestManagerFactory);
    return new Glide(...);
}
複製代碼

能夠看到內部先是構建了三個線程池,而後新建了一個Engine類實例,建立了RequestManagerRetriever實例,該實例就是調用getRequestManagerRetriever獲取到的實例。接着看看Glide的構造方法。oop

Glide(...) {
    registry = new Registry();
    // 這裏註冊了不少東西,好比編碼器、解碼器、工廠類、轉換器。這裏只是挑了幾個
    registry
        // 註冊Encoder 
        .append(ByteBuffer.class, new ByteBufferEncoder())
        // 註冊Decoder
        .append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
        // 註冊ModelLoaderFactory,三個參數分別是Model類型,Data類型,ModelLoader工廠
        .append(String.class, InputStream.class, new StringLoader.StreamFactory())
        // 註冊Transcoders
        .register(Bitmap.class,BitmapDrawable.class,new BitmapDrawableTranscoder(resources))
    ...
    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
    glideContext = new GlideContext(...);
}
複製代碼
  • 註冊Encoder最終會保存在registry的encoderRegistry變量中
  • 註冊Decoder最終會保存在registry的decoderRegistry變量中
  • 註冊ModelLoaderFactory最終會保存到registry的modelLoaderRegistry變量中的multiModelLoaderFactory變量中
  • 註冊Transcoders最終會保存到transcoderRegistry中

獲取到了RequestManagerRetriever實例後繼續調用其get方法fetch

public RequestManager get(@NonNull FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) {
        return get(activity.getApplicationContext());
    } else {
        assertNotDestroyed(activity);
        FragmentManager fm = activity.getSupportFragmentManager();
        return supportFragmentGet(
            activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
}
複製代碼

這裏先分析主線程調用的狀況,所以接着調用supportFragmentGetui

private RequestManager supportFragmentGet( @NonNull Context context, @NonNull FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
        Glide glide = Glide.get(context);
        requestManager =
            factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
        current.setRequestManager(requestManager);
    }
    return requestManager;
}
private SupportRequestManagerFragment getSupportRequestManagerFragment( @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    // 首先看看當前頁面是否已經有指定Fragment了,若是沒有那麼就新建一個而且加入到當前頁面中去
    SupportRequestManagerFragment current =
            (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
        current = pendingSupportRequestManagerFragments.get(fm);
        if (current == null) {
            current = new SupportRequestManagerFragment();
            current.setParentFragmentHint(parentHint);
            if (isParentVisible) {
                current.getGlideLifecycle().onStart();
            }
            pendingSupportRequestManagerFragments.put(fm, current);
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    }
    return current;
}
複製代碼

能夠看到supportFragmentGet首先將一個Fragment添加到當前頁面(用於觀察Activity的生命週期),而後建立一個RequestManager實例並返回,with方法結束而後看看load方法this

3、load()

load方法位於RequestManager中用於將指定資源做爲數據源這裏以參數爲String爲例

下面根據這個流程來看看源碼

// RequestManager.java
public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
}
複製代碼

asDrawable內部只是建立了一個RequestBuilder實例,接着再調用其load方法

// RequestBuilder.java
public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
}
複製代碼

load方法內部也就是進行了簡單賦值就將本身返回了,至此load方法也已經結束了,接着看看重點into方法

4、into()

into方法一樣定義在RequestBuilder中,主要用於將加載到的圖片傳遞給指定Target實例,這個方法內部至關複雜,也是本文的重點。

// RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    RequestOptions requestOptions = this.requestOptions;
    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:
        }
    }
    return into(
            glideContext.buildImageViewTarget(view, transcodeClass),
            /*targetListener=*/ null,
            requestOptions);
}
複製代碼

這裏會對RequestOptions作一些轉化,而後調用buildImageViewTarget構造指定的target。

// GlideContext.java
public <X> ViewTarget<ImageView, X> buildImageViewTarget( @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
// ImageViewTargetFactory.java
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
        return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
        return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
        throw new IllegalArgumentException(
            "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
}
複製代碼

因爲剛纔調用了asDrawable因此會新建一個DrawableImageViewTarget實例返回,接着繼續看看into方法

private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, @NonNull RequestOptions options) {
    if (!isModelSet) {
        throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    options = options.autoClone();
    Request request = buildRequest(target, targetListener, options);
    target.setRequest(request);
    requestManager.track(target, request);
    return target;
}
複製代碼

into方法內部首先判斷了是否調用了load方法而後調用buildRequest建立Request實例,而後調用track方法執行Request

private Request buildRequest( Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, RequestOptions requestOptions) {
    return buildRequestRecursive(...);
}
private Request buildRequestRecursive(...) {
    Request mainRequest =
            buildThumbnailRequestRecursive(...);
    if (errorRequestCoordinator == null) {
        return mainRequest;
    }
    Request errorRequest = errorBuilder.buildRequestRecursive(...);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
}
private Request buildThumbnailRequestRecursive(...) {
    // 只考慮沒有縮略圖的狀況了
    return obtainRequest(...);
}
private Request obtainRequest(...) {
    return SingleRequest.obtain(...);
}
複製代碼

能夠看到當沒有縮略圖的時候會經過調用SingleRequest.obtain新建一個SingleRequest實例,接着看看requestManager.track是怎麼執行Request的

// RequestManager.java
void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
}
public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
        request.begin();
    } else {
        request.clear();
        pendingRequests.add(request);
    }
}
複製代碼

若是沒有被暫定的話就接着繼續執行SingleRequest的begin方法

// SingleRequest.java
public void begin() {
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
    } else {
        target.getSize(this);
    }
}
複製代碼

根據是否設置了寬高來決定是調用onSizeReady仍是target.getSize,先假設沒有設置寬高來看看DrawableImageViewTarget實例的getSize方法

// getSize繼承自ViewTarget
public void getSize(@NonNull SizeReadyCallback cb) {
    sizeDeterminer.getSize(cb);
}
// SizeDeterminer.java
void getSize(@NonNull SizeReadyCallback cb) {
    int currentWidth = getTargetWidth();
    int currentHeight = getTargetHeight();
    if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
        cb.onSizeReady(currentWidth, currentHeight);
        return;
    }
    if (!cbs.contains(cb)) {
        cbs.add(cb);
    }
    if (layoutListener == null) {
        ViewTreeObserver observer = view.getViewTreeObserver();
        layoutListener = new SizeDeterminerLayoutListener(this);
        observer.addOnPreDrawListener(layoutListener);
    }
}
private int getTargetWidth() {
    int horizontalPadding = view.getPaddingLeft() + view.getPaddingRight();
    LayoutParams layoutParams = view.getLayoutParams();
    int layoutParamSize = layoutParams != null ? layoutParams.width : PENDING_SIZE;
    return getTargetDimen(view.getWidth(), layoutParamSize, horizontalPadding);
}
private int getTargetHeight() {
    int verticalPadding = view.getPaddingTop() + view.getPaddingBottom();
    LayoutParams layoutParams = view.getLayoutParams();
    int layoutParamSize = layoutParams != null ? layoutParams.height : PENDING_SIZE;
    return getTargetDimen(view.getHeight(), layoutParamSize, verticalPadding);
}
private int getTargetDimen(int viewSize, int paramSize, int paddingSize) {
    int adjustedParamSize = paramSize - paddingSize;
    if (adjustedParamSize > 0) {
        return adjustedParamSize;
    }
    if (waitForLayout && view.isLayoutRequested()) {
        return PENDING_SIZE;
    }
    int adjustedViewSize = viewSize - paddingSize;
    if (adjustedViewSize > 0) {
        return adjustedViewSize;
    }
    if (!view.isLayoutRequested() && paramSize == LayoutParams.WRAP_CONTENT) {
        return getMaxDisplayLength(view.getContext());
    }
    return PENDING_SIZE;
}
複製代碼

getSize內部經過獲取View的長寬減去對應的padding作爲目標寬高,而後又回調了onSizeReady方法,注意若是在此時目標ImageView尚未進行三大流程(也就是說獲取不到寬高)那麼會註冊回調,在回調中會調用onSizeReady方法這裏就不展開了,所以不論是否指定了寬高最終都會調用SingleRequest的onSizeReady方法

// SingleRequest.java
public void onSizeReady(int width, int height) {
    status = Status.RUNNING;
    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
    loadStatus = engine.load(...);
}
複製代碼

onSizeReady內部就分爲三步首先改變狀態爲Running而後計算最終須要加載的寬高,最後調用engine.load加載資源

public <R> LoadStatus load(...) {
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
            resourceClass, transcodeClass, options);
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
        cb.onResourceReady(active, DataSource.MEMORY_CACHE);
        if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
        }
        return null;
    }
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
        if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
        current.addCallback(cb);
        if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Added to existing load", startTime, key);
        }
        return new LoadStatus(cb, current);
    }
    EngineJob<R> engineJob =
            engineJobFactory.build(...);
    DecodeJob<R> decodeJob =
            decodeJobFactory.build(...);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(decodeJob);
    return new LoadStatus(cb, engineJob);
}
複製代碼

load方法首先構建一個EngineKey實例,內部只是重寫了hashCode和equal方法,接着若是容許讀取內存緩存就嘗試從ActivityResources中讀取讀不到再從MemoryCache中讀取,Tip: 暫時沒分清這二者的區別。若是已經有了當前key對應的EngineJob就直接返回,接着建立一個EngineJob和一個DecodeJob實例,而後調用engineJob.start

// EngineJob.java
public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
}
// DecodeJob.java
boolean willDecodeFromCache() {
    Stage firstStage = getNextStage(Stage.INITIALIZE);
    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
private Stage getNextStage(Stage current) {
    switch (current) {
        case INITIALIZE:
            return diskCacheStrategy.decodeCachedResource()
                ? Stage.RESOURCE_CACHE : 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;
        default:
            throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
}
複製代碼

首先根據是否從緩存中解碼選取線程池,接着使用線程池執行DecodeJob,而若是沒有設置DiskCacheStrategy那麼默認的DiskCacheStarategy.Automatic的decodeCachedResource返回true,因此willDecodeFromCache會返回true,因此最終executor會是diskCacheExecutor,而這個線程池就是在構建Glide實例的時候建立的diskCacheExecutor,接着看看DecodeJob的run方法

// DecodeJob.java
public void run() {
    DataFetcher<?> localFetcher = currentFetcher;
    try {
        if (isCancelled) {
            notifyFailed();
            return;
        }
        runWrapped();
    } catch (Throwable t) {
        ...
    }
}
複製代碼

若是取消了,就通知下失敗,不然主要就是調用了runWrapped

// DecodeJob.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;
        default:
            throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
}
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;
        default:
            throw new IllegalStateException("Unrecognized stage: " + stage);
    }
}
複製代碼

因爲DecodeJob實例剛剛建立因此runReason爲構造器時初始化的RunReason.INITIALIZE狀態,getNextStage返回Stage.RESOURCE_CACHE,getNextGenerator返回一個ResourceCacheGenerator實例,接着看看runGenerator方法

private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
        stage = getNextStage(stage);
        currentGenerator = getNextGenerator();
        if (stage == Stage.SOURCE) {
            reschedule();
            return;
        }
    }
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
        notifyFailed();
    }
}
複製代碼

內部調用了startNext方法,並根據其返回值判斷是否進入下一步,先來看看ResourceCacheGenerator的startNext方法

// ResourceCacheGenerator.java
public boolean startNext() {
    List<Key> sourceIds = helper.getCacheKeys();
    if (sourceIds.isEmpty()) {
        return false;
    }
    List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
    if (resourceClasses.isEmpty()) {
        if (File.class.equals(helper.getTranscodeClass())) {
            return false;
        }
    }
    while (modelLoaders == null || !hasNextModelLoader()) {
        resourceClassIndex++;
        if (resourceClassIndex >= resourceClasses.size()) {
            sourceIdIndex++;
            if (sourceIdIndex >= sourceIds.size()) {
                return false;
            }
            resourceClassIndex = 0;
        }
        // 獲取GlideUrl
        Key sourceId = sourceIds.get(sourceIdIndex);
        Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
        Transformation<?> transformation = helper.getTransformation(resourceClass);
        currentKey =
                new ResourceCacheKey(...);
        cacheFile = helper.getDiskCache().get(currentKey);
        if (cacheFile != null) {
            sourceKey = sourceId;
            modelLoaders = helper.getModelLoaders(cacheFile);
            modelLoaderIndex = 0;
        }
    }
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
        ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
        loadData = modelLoader.buildLoadData(cacheFile,
                helper.getWidth(), helper.getHeight(), helper.getOptions());
        if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
            started = true;
            loadData.fetcher.loadData(helper.getPriority(), this);
        }
    }
    return started;
}
複製代碼

getCacheKey這個方法內部很是複雜,一步步來進行分析

// DecoderHelp.java
List<Key> getCacheKeys() {
    if (!isCacheKeysSet) {
        isCacheKeysSet = true;
        cacheKeys.clear();
        List<LoadData<?>> loadData = getLoadData();
        for (int i = 0, size = loadData.size(); i < size; i++) {
            LoadData<?> data = loadData.get(i);
            if (!cacheKeys.contains(data.sourceKey)) {
                cacheKeys.add(data.sourceKey);
            }
            for (int j = 0; j < data.alternateKeys.size(); j++) {
                if (!cacheKeys.contains(data.alternateKeys.get(j))) {
                    cacheKeys.add(data.alternateKeys.get(j));
                }
            }
        }
    }
    return cacheKeys;
}
List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
        isLoadDataSet = true;
        loadData.clear();
        List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
        for (int i = 0, size = modelLoaders.size(); i < size; i++) {
            ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
            LoadData<?> current =
                    modelLoader.buildLoadData(model, width, height, options);
            if (current != null) {
                loadData.add(current);
            }
        }
    }
    return loadData;
}
// Registry.java
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
    List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
    if (result.isEmpty()) {
        throw new NoModelLoaderAvailableException(model);
    }
    return result;
}

public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
    List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
    int size = modelLoaders.size();
    boolean isEmpty = true;
    List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
    for (int i = 0; i < size; i++) {
        ModelLoader<A, ?> loader = modelLoaders.get(i);
        if (loader.handles(model)) {
            if (isEmpty) {
                filteredLoaders = new ArrayList<>(size - i);
                isEmpty = false;
            }
            filteredLoaders.add(loader);
        }
    }
    return filteredLoaders;
}
private synchronized <A> List<ModelLoader<A, ?>> getModelLoadersForClass(
            @NonNull Class<A> modelClass) {
    List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
    if (loaders == null) {
        loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
        cache.put(modelClass, loaders);
    }
    return loaders;
}
// MultiModelLoaderFactory.java
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
    try {
        List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
        for (Entry<?, ?> entry : entries) {
            if (alreadyUsedEntries.contains(entry)) {
                continue;
            }
            if (entry.handles(modelClass)) {
                alreadyUsedEntries.add(entry);
                // 這個地方可能會觸發遞歸,爲了防止棧溢出,用過的entry不容許再用
                loaders.add(this.<Model, Object>build(entry));
                alreadyUsedEntries.remove(entry);
            }
        }
        return loaders;
    } catch (Throwable t) {
        alreadyUsedEntries.clear();
        throw t;
    }
}
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
    return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}
複製代碼

1.1 Model(String) ——> Date(Any)

getCacheKeys內部的一個getLoadData的代碼就很長,通過層層調用最終會調用到build方法從Glide實例一建立就註冊的的全部Factory中找尋到全部能處理Model類型爲String的Factory,也就是以下四個

.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
複製代碼

2.1 Model(String) ——> Data(InputStream): DataUrlLoader

這四個來一個個進行分析(爲何要一個個分析接着看就知道了),首先會調用到DataUrlLoader.StreamFactory的build方法

// DataUrlLoader.StreamFactory.java
public ModelLoader<Model, InputStream> build( @NonNull MultiModelLoaderFactory multiFactory) {
  return new DataUrlLoader<>(opener);
}
複製代碼

內部簡單的建立了一個DataUrlLoader實例,而後將其放入到了loaders中。

2.2 Model(String) ——> Model(Uri) ----> Data(InputStream)

接着看看第二個StringLoader.StreamFactory的build方法

// StringLoader.StreamFactory.java
public ModelLoader<String, InputStream> build( @NonNull MultiModelLoaderFactory multiFactory) {
  return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
複製代碼

此次跟DataUrlLoader.StreamFactory不同了,內部建立了一個StringLoader實例,可是又回調了MultiModelLoaderFactory兩個參數的build方法,而且明確指定了要尋找能將Model類型爲Uri的處理成Data類型爲InputStream的Factory,先來看看兩個參數的build方法

// MultiModelLoaderFactory.java
public synchronized <Model, Data> ModelLoader<Model, Data> build(Class<Model> modelClass, Class<DatdataClass) {
    try {
        List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
        boolean ignoredAnyEntries = false;
        for (Entry<?, ?> entry : entries) {
            if (alreadyUsedEntries.contains(entry)) {
                ignoredAnyEntries = true;
                continue;
            }
            if (entry.handles(modelClass, dataClass)) {
                alreadyUsedEntries.add(entry);
                loaders.add(this.<Model, Data>build(entry));
                alreadyUsedEntries.remove(entry);
            }
        }
        if (loaders.size() > 1) {
            return factory.build(loaders, throwableListPool);
        } else if (loaders.size() == 1) {
            return loaders.get(0);
        } else {
            if (ignoredAnyEntries) {
                return emptyModelLoader();
            } else {
                throw new NoModelLoaderAvailableException(modelClass, dataClass);
            }
        }
    } catch (Throwable t) {
        alreadyUsedEntries.clear();
        throw t;
    }
}
複製代碼

代碼基本與一個參數的build方法相似,不一樣點在於明確指定了目標Data類型和返回類型,那麼以前在註冊列表中尋找Model類型爲Uri,Data類型爲InputStream的factory,以下所示

.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
複製代碼

有7個Factory知足條件,那麼依次再來看這7個Factory的build方法

2.2.1 Model(String) ——> Model(Uri) ——> Data(InputStream): DataUrlLoader

// DataUrlLoader.StreamFactory.java
public ModelLoader<Model, InputStream> build( @NonNull MultiModelLoaderFactory multiFactory) {
  return new DataUrlLoader<>(opener);
}
複製代碼

這個沒什麼特別的接着看下一個

2.2.2 Model(String) ——> Model(Uri): HttpUriLoader ---> Model(GlideUrl) ——> Data(InputStream)

// HttpUriLoader.Factory.java
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
}
複製代碼
2.2.2.1 Model(String) ——> Model(Uri) ——> Model(GlideUrl) ——> Data(InputStream)

構建HttpUrlLoader的時候又要去尋找能將Model類型爲GlideUrl處理成Data類型爲InputStream的Factory,那麼接着看,經過查詢註冊,只發現瞭如下一個。

.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
複製代碼

那麼直接看看其build方法

// HttpGlideUrlLoader.Factory.java
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new HttpGlideUrlLoader(modelCache);
}
複製代碼

只是建立了一個HttpGlideUrlLoader實例就返回(這個實例纔是真正的去請求網絡數據),接着看其他5個能將Model(Uri)處理成Data(InputStream)的Factory

2.2.3 Model(String) ——> Model(Uri) ——> Data(InputStream): StreamAssetPathFetcher

// AssetUriLoader.StreamFactory.java
public DataFetcher<InputStream> buildFetcher(AssetManager assetManager, String assetPath) {
    return new StreamAssetPathFetcher(assetManager, assetPath);
}
複製代碼

方法內部僅僅建立了一個StreamAssetPathFetcher實例接着看看下一個

2.2.4 Model(String) ——> Model(Uri) ——> Data(InputStream): MediaStoreImageThumbLoader

// MediaStoreImageThumbLoader.Factory
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new MediaStoreImageThumbLoader(context);
}
複製代碼

方法內部僅僅建立了一個MediaStoreImageThumbLoader實例接着看看下一個

2.2.5 Model(String) ——> Model(Uri) ——> Data(InputStream): MediaStoreVideoThumbLoader

// MediaStoreVideoThumbLoader.java
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new MediaStoreVideoThumbLoader(context);
}
複製代碼

方法內部僅僅建立了一個MediaStoreVideoThumbLoader實例接着看看下一個

2.2.6 Model(String) ——> Model(Uri) ——> Data(InputStream): UriLoader

public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new UriLoader<>(this);
}
複製代碼

方法內部僅僅建立了一個UriLoader實例接着看看最後一個

2.2.7 Model(String) ——> Model(Uri): UrlUriLoader ---> Model(GlideUrl) ——> Data(InputStream)

public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
    return new UrlUriLoader<>(multiFactory.build(GlideUrl.class, InputStream.class));
}
複製代碼

內部又尋找能將GlideUrl處理成InputStream的Factory,通過上面的分析也就只有HttpGlideUrlLoader.Factory能進行處理這裏進再也不展開。

2.3 Model(String) ——> Model(Uri) ---> Data(ParcelFileDescriptor)

接着看能將Model(String)處理成Data(Any)的第三個Factory(StringLoader.FileDescriptorFactory)

// StringLoader.FileDescriptorFactory
public ModelLoader<String, ParcelFileDescriptor> build( @NonNull MultiModelLoaderFactory multiFactory) {
    return new StringLoader<>(multiFactory.build(Uri.class, ParcelFileDescriptor.class));
}
複製代碼

內部再去尋找能將Uri處理成ParcelFileDescriptor的Factory,經過查詢有如下兩個

.append(Uri.class,ParcelFileDescriptor.class, new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, ParcelFileDescriptor.class, new UriLoader.FileDescriptorFactory(contentResolver))
複製代碼

再來分別看看

2.3.1 Model(String) ——> Model(Uri) ——> Data(ParcelFileDescriptor): AssetUriLoader

// AssetUriLoader.FileDescriptorFactory.java
public ModelLoader<Uri, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
    return new AssetUriLoader<>(assetManager, this);
}
複製代碼

內部僅僅建立了AssetUriLoader,接着看下一個

2.3.2 Model(String) ——> Model(Uri) ——> Data(ParcelFileDescriptor): UriLoader

// UriLoader.FileDescriptorFactory.java
public ModelLoader<Uri, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
    return new UriLoader<>(this);
}
複製代碼

2.4 Model(String) ——> Model(Uri) ---> Data(AssetFileDescriptor)

接着看能將Model(String)處理成Data(Any)的最後一個Factory(StringLoader.AssetFileDescriptorFactory)

// StringLoader.AssetFileDescriptorFactory.java
public ModelLoader<String, AssetFileDescriptor> build( @NonNull MultiModelLoaderFactory multiFactory) {
    return new StringLoader<>(multiFactory.build(Uri.class, AssetFileDescriptor.class));
}
複製代碼

內部又去尋找能將Uri處理成AssetFileDescriptor的Factory,經過查詢就找到了下面這一個

.append(Uri.class, AssetFileDescriptor.class, new UriLoader.AssetFileDescriptorFactory(contentResolver))
複製代碼

2.4.1 Model(String) ——> Model(Uri) ——> Data(AssetFileDescriptor): UriLoader

// UriLoad.AssetFileDescriptorFactory.java
public ModelLoader<Uri, AssetFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
    return new UriLoader<>(this);
}
複製代碼

而後調用入參爲Entry的build方法,內部調用了entry.factory.build(this),也就是分別調用上述4個Factory的build方法。到如今爲止也就剛剛分析好了MultiModelLoaderFactory.build(String.class)這麼一個方法調用,先來總結下。

3 總結

能夠看到Glide會尋找全部能處理Model類型String的Factory調用其build方建立ModelLoader,可是有些ModelLoader單獨也不能完成一個轉換,所以這些ModelLoader一般會有另外一個能幫助其完成轉換的ModelLoader實例,來舉個例子,StringLoader沒辦法將Model(String)處理成Data(InputSteam),所以其持有了一個能將Model(Uri)處理能Data(InputStream)的ModelLoad,StringLoader主要功能就是在外界調用buildLoadData時將String類型的Url轉換爲Uri而後調用那個持有的ModelLoader的buildLoadData方法,最終可能的轉化路線以下所示

  • Model(String) ——> Data(InputStream)
  • Model(String) ——> Model(Uri) ——> Data(InputStream)
  • Model(String) ——> Model(Uri) ——> Model(GlideUrl) ——> Data(InputStream)
  • Model(String) ——> Model(Uri) ——> Data(ParcelFileDescriptor)
  • Model(String) ——> Model(Uri) ——> Data(AssetFileDescriptor)

接着再回到getModelLoaders裏面執行剩餘代碼

public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
    List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
    int size = modelLoaders.size();
    boolean isEmpty = true;
    List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
    for (int i = 0; i < size; i++) {
        ModelLoader<A, ?> loader = modelLoaders.get(i);
        if (loader.handles(model)) {
            if (isEmpty) {
                filteredLoaders = new ArrayList<>(size - i);
                isEmpty = false;
            }
            filteredLoaders.add(loader);
        }
    }
    return filteredLoaders;
}
// DataUrlLoader.java
public boolean handles(@NonNull Model model) {
    return model.toString().startsWith(DATA_SCHEME_IMAGE);
}
複製代碼

MultiModelLoaderFactory.build只是從全部Factory中找尋出全部能處理Model(String)的Factory,可是有些ModelLoader雖然說也能處理Model(String),可是擁有其侷限性好比DataUrlLoader.StreamFactory.build產生的DataUrlLoader其只能處理以data:image開頭的字符串,所以當該方法返回時還餘下3個ModelLoader,接着再回到DecodeHelp.getLoadData中

// DecodeHelper.java
List<LoadData<?>> getLoadData() {
    ...
    List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
    for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current =
            modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
            loadData.add(current);
        }
    }
    return loadData;
}
複製代碼

三個StringLoader.buildLoadData

內部分別調用了三個StringLoader的buildLoadData來構建LoadData實例

public LoadData<Data> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
    Uri uri = parseUri(model);
    if (uri == null || !uriLoader.handles(uri)) {
      return null;
    }
    return uriLoader.buildLoadData(uri, width, height, options);
}
複製代碼

能夠看到首先將傳入的String類型的url轉化爲Uri實例,而後調用uriLoader.buildLoadData構建LoadData,一個典型的代理模式,通過前面的分析uriLoader實際上是一個MutiModelLoader(內部有7個ModelLoader,這7個都能將Model(Uri)處理成Data(InputStream))

// MultiModelLoader.java
public LoadData<Data> buildLoadData(@NonNull Model model, int width, int height, @NonNull Options options) {
    Key sourceKey = null;
    int size = modelLoaders.size();
    List<DataFetcher<Data>> fetchers = new ArrayList<>(size);
    for (int i = 0; i < size; i++) {
        ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);
        if (modelLoader.handles(model)) {
            LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);
            if (loadData != null) {
                sourceKey = loadData.sourceKey;
                fetchers.add(loadData.fetcher);
            }
        }
    }
    return !fetchers.isEmpty() && sourceKey != null
            ? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool)) : null;
}
複製代碼

首先第一個DataUrlLoader因爲只能處理data:image開頭的字符串,因此留下6個,來看看第二個HttpUriLoader

public LoadData<InputStream> buildLoadData(@NonNull Uri model, int width, int height, @NonNull Options options) {
    return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);
}
複製代碼

哎,又跑去調用urlLoader.buildLoadData一層層真深,這個urlLoader仍是包含了HttpGlideUrlLoader對應於前面講的2.2.2.1

// HttpGlideUrlLoader.java
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
    GlideUrl url = model;
    if (modelCache != null) {
        url = modelCache.get(model, 0, 0);
        if (url == null) {
            modelCache.put(model, 0, 0, model);
            url = model;
        }
    }
    int timeout = options.get(TIMEOUT);
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
複製代碼

千辛萬苦找到了返回的第一個LoadData,注意內部傳入了一個HttpUrlFetcher,後面會用於網絡請求,繼續第三個AssetUriLoader

// AssetUriLoader.java
public boolean handles(@NonNull Uri model) {
    return ContentResolver.SCHEME_FILE.equals(model.getScheme()) && !model.getPathSegments()
        .isEmpty() && ASSET_PATH_SEGMENT.equals(model.getPathSegments().get(0));
}
複製代碼

很明顯model的scheme是http因此AssetUriLoader不知足,相似的後面三個MediaStoreImageThumbLoader、MediaStoreVideoThumbLoader、UriLoader也無法處理,直接看最後一個UrlUriLoader

// UrlUriLoader.java
private static final Set<String> SCHEMES = Collections.unmodifiableSet(
    new HashSet<>(
        Arrays.asList(
          "http",
          "https"
        )
    )
);
public boolean handles(@NonNull Uri uri) {
    return SCHEMES.contains(uri.getScheme());
}
複製代碼

很明顯能夠處理,繼續看其buildLoadData方法

public LoadData<Data> buildLoadData(@NonNull Uri uri, int width, int height, @NonNull Options options) {
    GlideUrl glideUrl = new GlideUrl(uri.toString());
    return urlLoader.buildLoadData(glideUrl, width, height, options);
}
複製代碼

構建了一個GlideUrl實例,這裏的urlLoader是一個HttpGlideUrlLoader實例對應於2.2.7,這個結果與第二個HttpUriLoader處理結果一致,第一個StringLoader到此結束,一共返回兩個DataFetcher,都是HttpUrlFetcher,封裝成一個LoadData(MultiFetcher)返回,後面的第2、第三個ModelLoader都不符合條件,因此最後getLoadData只會返回一個LoadData,那麼getCacheKeys也就只會返回擁有一個Key的列表,繼續回到ResourceCacheGenerator.startNext,方法後面根據全部註冊的decoder、modelLoader、transCodeClass查詢到全部能夠將Data轉化爲對應Resource的類,最後因爲沒有緩存startNext最終會返回false,DataCacheGenerator也是一樣的道理,最後走到SourceGenerator中

// SourceGenerator.java
public boolean startNext() {
    ...
    while (!started && hasNextModelLoader()) {
        loadData = helper.getLoadData().get(loadDataListIndex++);
        if (loadData != null
            && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
            || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
            started = true;
            loadData.fetcher.loadData(helper.getPriority(), this);
        }
    }
    return started;
}
複製代碼

注意這裏helper.getLoadData()獲取到的就是剛開始ResourceCacheGenerator調用getLoadData保存在其中的一個列表,接着會調用fetcher.loadData,前面已經分析過fetcher就是MultiFetcher,內部擁有兩個HttpUrlFetcher

// MultiFetcher.java
public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
    this.priority = priority;
    this.callback = callback;
    exceptions = throwableListPool.acquire();
    fetchers.get(currentIndex).loadData(priority, this);
}
複製代碼

這裏只會啓動第一個HttpUrlFetcher,第二個只會在第一個失敗的時候纔會調用,繼續看看HttpUrlFetcher.loadData

public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
        InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
        callback.onDataReady(result);
    } catch (IOException e) {
        callback.onLoadFailed(e);
    }
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
    ...
    urlConnection = connectionFactory.build(url);
    ...
    urlConnection.setInstanceFollowRedirects(false);
    urlConnection.connect();
    stream = urlConnection.getInputStream();
    if (isCancelled) {
        return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
        return getStreamForSuccessfulRequest(urlConnection);
    } else if (isHttpRedirect(statusCode)) {
        ...
        return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
    }
    ...
}
複製代碼

內部也就是直接使用了HttpUrlConnection,而且支持重定向(最多5次),若是請求成功了則會返回一個InputStream實例,而且回調callback.onDataReady,這裏的callback就是MultiFetcher

// MultiFetcher.java
public void onDataReady(@Nullable Data data) {
    if (data != null) {
        callback.onDataReady(data);
    } else {
        startNextOrFail();
    }
}
複製代碼

若是加載成功了那麼再回調onDataReady,否則就使用下一個Fetcher請求數據或者只會返回錯誤,這裏只看正返回成功的狀況,callback是SourceGenerator

public void onDataReady(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);
    }
}
複製代碼

默認的DiskCacheStrategy是AUTOMATIC,因此傳入的REMOTE知足第一個If條件,cb是DecodeJob實例

// DecodeJob.java
public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
}
public void reschedule(DecodeJob<?> job) {
    getActiveSourceExecutor().execute(job);
}
複製代碼

接着又會執行這個DecodeJob,不過此次與第一次不一樣,此次調用是DecodeJob不是新建的其成員都維持着 執行順序 DecodeJob.run -> runWrapped -> runGenerators -> SourceGenerator.startNext

// SourceGenerator.java
public boolean startNext() {
    if (dataToCache != null) {
        Object data = dataToCache;
        dataToCache = null;
        cacheData(data);
    }
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
        return true;
    }
}
複製代碼

此次dataToCache不是null了而是一個InputStream實例

private void cacheData(Object dataToCache) {
    long startTime = LogTime.getLogTime();
    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);
        ...
    } finally {
        loadData.fetcher.cleanup();
    }
    sourceCacheGenerator =
            new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
複製代碼

首先獲取到能處理InoutStream的Encoder也就是StreamEncoder,接着內部會調用到DiskLruCacheWrapper.put,方法內部又會執行Encoder.encode將輸入流寫入到文件中,這就不展開了。而且最後將sourceCacheGenerator進行了賦值(注意在建立DataCacheGenerator時傳入的callback是SourceGenerator自己),因此會執行DataCacheGenerator.startNext

// DataCacheGenerator.java
public boolean startNext() {
    while (modelLoaders == null || !hasNextModelLoader()) {
        sourceIdIndex++;
        if (sourceIdIndex >= cacheKeys.size()) {
            return false;
        }
        Key sourceId = cacheKeys.get(sourceIdIndex);
        Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
        cacheFile = helper.getDiskCache().get(originalKey);
        if (cacheFile != null) {
            this.sourceKey = sourceId;
            modelLoaders = helper.getModelLoaders(cacheFile);
            modelLoaderIndex = 0;
        }
    }
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
        ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
        loadData =
                modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
                        helper.getOptions());
        if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
            started = true;
            loadData.fetcher.loadData(helper.getPriority(), this);
        }
    }
    return started;
}
複製代碼

內部根據緩存Key獲取到剛剛保存的緩存文件,而後獲取全部能處理Model(File)類型的ModelLoader,接着將其遍歷構建LoadData,這裏就不看getModelLoaders內部實現邏輯,直接用ByteBufferFileLoader進行分析,經過調用buildLoadData會構建出一個ByteBufferFetcher實例,接着看看其loadData方法

// ByteBufferFetcher.java
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
    ByteBuffer result;
    try {
        result = ByteBufferUtil.fromFile(file);
    } catch (IOException e) {
        callback.onLoadFailed(e);
        return;
    }
    callback.onDataReady(result);
}
複製代碼

將文件中全部的內容所有讀取到ByteBuffer中後回調onDataReady方法內部通過一層層調用最終調用了DecodeJob.decodeFromRetrievedData,到如今爲止數據還所有在這個byteBuffer中去,接下來就是尋找對應的decoder將其加載成一個Bitmap而後設置給ImageView,通過一系列判斷,最終會尋找到ByteBufferBitmapDecoder

public Resource<Bitmap> decode(@NonNull ByteBuffer source, int width, int height, @NonNull Options options) throws IOException {
    InputStream is = ByteBufferUtil.toStream(source);
    return downsampler.decode(is, width, height, options);
}
複製代碼

downSampler裏面作了真正的操做,好比縮放圖片適應當前的ImageView大小等,接着判斷是否要將轉化後的圖片進行緩存,最後就通知外界圖片加載成功。

// DecodeJob.java
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
}
// EngineJob.java
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    this.resource = resource;
    this.dataSource = dataSource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private static final Handler MAIN_THREAD_HANDLER =
      new Handler(Looper.getMainLooper(), new MainThreadCallback());
複製代碼

那麼最後接受消息的就是MainThreadCallback

public boolean handleMessage(Message message) {
    EngineJob<?> job = (EngineJob<?>) message.obj;
    switch (message.what) {
        case MSG_COMPLETE:
            job.handleResultOnMainThread();
            break;
        ...
        default:
            throw new IllegalStateException("Unrecognized message: " + message.what);
    }
    return true;
}
複製代碼

這裏最終通過一層層回調會在主線程調用ImageViewTarget.onResourceReady

// ImageViewTarget.java
 public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
        setResourceInternal(resource);
    } else {
        maybeUpdateAnimatable(resource);
    }
}
private void setResourceInternal(@Nullable Z resource) {
    setResource(resource);
    maybeUpdateAnimatable(resource);
}
protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
}
複製代碼

到此爲止一張圖片就加載完畢了

相關文章
相關標籤/搜索