源碼分析--Glide源碼 加載圖片 二 into 網(wǎng)絡(luò)下載圖片的過程

上一篇博文中講了with,load的過程,因?yàn)榇蟛糠侄际浅跏荚?,所以總的來講還是比較好理解的,這篇博文講的是into的過程,這個過程就會用到前兩個步初始話的對象,這一步也是Glide的核心、難點(diǎn)所在,本文較長如果能耐心讀下去,應(yīng)該會有相應(yīng)的收獲的。
如果你發(fā)現(xiàn)本文中有任何錯誤,請?jiān)谠u論區(qū)留言或者私信我,我會第一時間改正,謝謝!

into

Glide.with(MainActivity.this).load(s).into(imageView);
傳入的一般都是ImageView,進(jìn)入到into源碼中看一下

//DrawableRequestBuilder.java
public Target<GlideDrawable> into(ImageView view) {
        return super.into(view);
    }

//GenericRequestBuilder.java
 public Target<TranscodeType> into(ImageView view) {
        Util.assertMainThread();
        if (view == null) {
            throw new IllegalArgumentException("You must pass in a non null View");
        }

        if (!isTransformationSet && view.getScaleType() != null) {
            switch (view.getScaleType()) {
                case CENTER_CROP:
                    applyCenterCrop();
                    break;
                case FIT_CENTER:
                case FIT_START:
                case FIT_END:
                    applyFitCenter();
                    break;
                //$CASES-OMITTED$
                default:
                    // Do nothing.
            }
        }
        return into(glide.buildImageViewTarget(view, transcodeClass));
    }

首先,這里先獲取了ScaleType,并做了相應(yīng)的處理,然后執(zhí)行了glide.buildImageViewTarget(view, transcodeClass)這個方法,跟進(jìn)看一下,最后執(zhí)行了這個方法

 public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
        if (GlideDrawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new GlideDrawableImageViewTarget(view);
        } else if (Bitmap.class.equals(clazz)) {
            return (Target<Z>) new BitmapImageViewTarget(view);
        } else if (Drawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new DrawableImageViewTarget(view);
        } else {
            throw new IllegalArgumentException("Unhandled class: " + clazz
                    + ", try .as*(Class).transcode(ResourceTranscoder)");
        }
    }

而我們傳入的參數(shù)一個是ImageView,另外一個是在load中的loadGeneric()方法生成的 GlideDrawable.class,那么這里返回的是GlideDrawableImageViewTarget對象,這個類進(jìn)去看一下,發(fā)現(xiàn)這個類有對ImageView的setImageDrawable操作,所以盲猜一手最后會調(diào)用這個類設(shè)置圖片資源。
繼續(xù)回去看into方法集體干了什么

public <Y extends Target<TranscodeType>> Y into(Y target) {
    Util.assertMainThread();
    if (target == null) {
        throw new IllegalArgumentException("You must pass in a non null Target");
    }
    if (!isModelSet) {
        throw new IllegalArgumentException("You must first set a model (try #load())");
    }
    //獲取這個target之前的request
    Request previous = target.getRequest();
    //如果有的話 就進(jìn)行清理 
    if (previous != null) {
        previous.clear();
        requestTracker.removeRequest(previous);
        previous.recycle();
    }
    //創(chuàng)建本次的request
    Request request = buildRequest(target);//進(jìn)去看一下
    target.setRequest(request);
    lifecycle.addListener(target);
    requestTracker.runRequest(request);

    return target;
}

具體操作加了注釋了,關(guān)注一下buildrequest方法

private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
    if (thumbnailRequestBuilder != null) {
        if (isThumbnailBuilt) {
            throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
                    + "consider using clone() on the request(s) passed to thumbnail()");
        }
        // Recursive case: contains a potentially recursive thumbnail request builder.
        if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
            thumbnailRequestBuilder.animationFactory = animationFactory;
        }

        if (thumbnailRequestBuilder.priority == null) {
            thumbnailRequestBuilder.priority = getThumbnailPriority();
        }

        if (Util.isValidDimensions(overrideWidth, overrideHeight)
                && !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
                        thumbnailRequestBuilder.overrideHeight)) {
          thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
        }

        ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
        Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
        // Guard against infinite recursion.
        isThumbnailBuilt = true;
        // Recursively generate thumbnail requests.
        Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
        isThumbnailBuilt = false;
        coordinator.setRequests(fullRequest, thumbRequest);
        return coordinator;
    } else if (thumbSizeMultiplier != null) {
        // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
        ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
        Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
        Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
        coordinator.setRequests(fullRequest, thumbnailRequest);
        return coordinator;
    } else {
        //最后進(jìn)入這里,也可以看到基本上都會調(diào)用這個方法
        // Base case: no thumbnail.
        return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
        //生成一個GenericRequest對象返回
    }
}

private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
        RequestCoordinator requestCoordinator) {
    return GenericRequest.obtain(
            loadProvider,
            model,
            signature,
            context,
            priority,
            target,
            sizeMultiplier,
            placeholderDrawable,
            placeholderId,
            errorPlaceholder,
            errorId,
            fallbackDrawable,
            fallbackResource,
            requestListener,
            requestCoordinator,
            glide.getEngine(),
            transformation,
            transcodeClass,
            isCacheable,
            animationFactory,
            overrideWidth,
            overrideHeight,
            diskCacheStrategy);
}

可以看到最后創(chuàng)建了一個GenericRequest,返回到之前的into方法,看創(chuàng)建完request之后有干了什么

    //創(chuàng)建本次的request
    Request request = buildRequest(target);//進(jìn)去看一下
    target.setRequest(request);
    lifecycle.addListener(target);
    requestTracker.runRequest(request);
    
    return target;

requestTracker看做是一個管理request集合的類,看一下具體的方法

//RequestTracker.java
public void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
        //執(zhí)行request
        request.begin();
    } else {
        pendingRequests.add(request);
    }
}

看一下具體的begin方法執(zhí)行了什么

//GenericRequest.java
/**
 * {@inheritDoc}
 */
@Override
public void begin() {
    startTime = LogTime.getLogTime();
    if (model == null) {
        onException(null);
        return;
    }

    status = Status.WAITING_FOR_SIZE;
    //測量長寬最終都會走到 onSizeReady方法
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
    } else {
        target.getSize(this);
    }

    if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
        //設(shè)置占位符(圖片沒有加載成功時 加載中的那張圖片)
        target.onLoadStarted(getPlaceholderDrawable());
    }
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
}

可以看到這里設(shè)置了占位圖片,同時也測量了寬高,并回調(diào)了onSizeReady方法。

@Override
public void onSizeReady(int width, int height) {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    if (status != Status.WAITING_FOR_SIZE) {
        return;
    }
    status = Status.RUNNING;

    width = Math.round(sizeMultiplier * width);
    height = Math.round(sizeMultiplier * height);
    //關(guān)鍵
    ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
    final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);

    if (dataFetcher == null) {
        onException(new Exception("Failed to load model: \'" + model + "\'"));
        return;
    }
    ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
    }
    loadedFromMemoryCache = true;
    //通過engine來load圖片,我們需要關(guān)注的是這個方法及上面的傳值
    loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
            priority, isMemoryCacheable, diskCacheStrategy, this);
    loadedFromMemoryCache = resource != null;
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
}

到這里,難點(diǎn)就來了,首先先要了解他這里的幾個參數(shù)具體是什么。
第一個 loadProvider

DrawableTypeRequest.java
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
        ModelLoader<A, InputStream> streamModelLoader,
        ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
        Class<R> transcodedClass,
        ResourceTranscoder<Z, R> transcoder) {
    if (streamModelLoader == null && fileDescriptorModelLoader == null) {
        return null;
    }

    if (transcoder == null) {
        transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
    }
    DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
            resourceClass);
    ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
            fileDescriptorModelLoader);
    return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}

就是我們在load最后生成的FixedLoadProvider。那么
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader() 獲取到的ModelLoader為ImageVideoModelLoader
DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height); dataFetcher是ImageVideoFetcher

接下來就是主菜load過程了

//Engine.java
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
        DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
        Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = LogTime.getLogTime();

    final String id = fetcher.getId();
    //根據(jù)長寬等條件生成一個key
    EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
            loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
            transcoder, loadProvider.getSourceEncoder());
    //根據(jù)上面生成的key向緩存中讀取資源
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }

    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
        cb.onResourceReady(active);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
        }
        return null;
    }

    EngineJob current = jobs.get(key);
    if (current != null) {
        current.addCallback(cb);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Added to existing load", startTime, key);
        }
        return new LoadStatus(cb, current);
    }

    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
    DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
            transcoder, diskCacheProvider, diskCacheStrategy, priority);
    EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(runnable);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
}

這個方法很長,一步步看,首先看一下獲取緩存的方法

EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
        return null;
    }

    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
        cached.acquire();
        activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
    }
    return cached;
}

@SuppressWarnings("unchecked")
private EngineResource<?> getEngineResourceFromCache(Key key) {
    Resource<?> cached = cache.remove(key);

    final EngineResource result;
    if (cached == null) {
        result = null;
    } else if (cached instanceof EngineResource) {
        // Save an object allocation if we've cached an EngineResource (the typical case).
        result = (EngineResource) cached;
    } else {
        result = new EngineResource(cached, true /*isCacheable*/);
    }
    return result;
}

根據(jù)這一串代碼和下面的注釋可以推斷出是從內(nèi)存中直接獲取出來的,通過追蹤我們可以了解到這個是從LruResourceCache中獲取出來的,看一下LruResourceCache的構(gòu)造

public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
    private ResourceRemovedListener listener;
    ......

這里就不先展開了講了,先知道有緩存這個東西,重點(diǎn)走一遍完整的網(wǎng)絡(luò)請求流程。再重新回到load方法

 //看看有沒有這個key正在執(zhí)行的方法,有的話就不new了,直接更新新的監(jiān)聽
  EngineJob current = jobs.get(key);
    if (current != null) {
        current.addCallback(cb);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Added to existing load", startTime, key);
        }
        return new LoadStatus(cb, current);
    }
    //通過engineJobFactory根據(jù)key新建一個engineJob
    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
    DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
            transcoder, diskCacheProvider, diskCacheStrategy, priority);
    //創(chuàng)建一個EngineRunnable,這個是實(shí)現(xiàn)圖片加載主要的類
    EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    //放到正在請求的隊(duì)列中
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    //啟動EngineRunnable線程
    engineJob.start(runnable);

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);

可以看到這里又建了幾個類,我們拿出來看看
EngineJob:執(zhí)行獲取圖片的類,相當(dāng)于小包工頭
EngineRunnable:執(zhí)行從內(nèi)存,網(wǎng)絡(luò)中獲取圖片的類,相當(dāng)于小包工頭(EngineJob)下面的建筑工人組長
DecodeJob:真正執(zhí)行內(nèi)存,網(wǎng)絡(luò)中獲取圖片工作的類,相當(dāng)于上面兩個管理下的工人

先看一下engineJob.start的實(shí)現(xiàn)

    private final ExecutorService diskCacheService;
    public void start(EngineRunnable engineRunnable) {
        this.engineRunnable = engineRunnable;
        future = diskCacheService.submit(engineRunnable);
    }

先用diskCacheService執(zhí)行了這個方法,diskCacheService這個對象根據(jù)名字可以看出是專門用來執(zhí)行硬盤緩存的線程池,那么接下來看一下EngineRunnable的方法

//EngineRunnable.java
@Override
public void run() {
    if (isCancelled) {
        return;
    }

    Exception exception = null;
    Resource<?> resource = null;
    try {
        resource = decode();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Exception decoding", e);
        }
        exception = e;
    }

    if (isCancelled) {
        if (resource != null) {
            resource.recycle();
        }
        return;
    }
    
    if (resource == null) {
        onLoadFailed(exception);
    } else {
        onLoadComplete(resource);
    }
}

這個方法看上去就很清晰了,通過 decode()方法獲取到資源,最后通過onLoadFailed,onLoadComplete來返回結(jié)果,需要看一下decode方法

private Resource<?> decode() throws Exception {
    //不設(shè)置的話這個判斷第一次是會放回true
    if (isDecodingFromCache()) {
        return decodeFromCache();
    } else {
        return decodeFromSource();
    }
}

//這個方法主要是用來判斷當(dāng)前執(zhí)行的是不是從cache中獲取,第一次進(jìn)來的時候默認(rèn)都是從cache中獲取
private boolean isDecodingFromCache() {
    return stage == Stage.CACHE;
}

接下來看一下decodeFromCache方法

private Resource<?> decodeFromCache() throws Exception {
    Resource<?> result = null;
    try {
        result = decodeJob.decodeResultFromCache();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Exception decoding result from cache: " + e);
        }
    }

    if (result == null) {
        result = decodeJob.decodeSourceFromCache();
    }
    return result;
}

//decodeJob.java
/**
 * Returns a transcoded resource decoded from transformed resource data in the disk cache, or null if no such
 * resource exists.
 *
 * @throws Exception
 */
public Resource<Z> decodeResultFromCache() throws Exception {
    if (!diskCacheStrategy.cacheResult()) {
        return null;
    }

    long startTime = LogTime.getLogTime();
    Resource<T> transformed = loadFromCache(resultKey);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Decoded transformed from cache", startTime);
    }
    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transcoded transformed from cache", startTime);
    }
    return result;
}

private Resource<T> loadFromCache(Key key) throws IOException {
    File cacheFile = diskCacheProvider.getDiskCache().get(key);
    if (cacheFile == null) {
        return null;
    }

    Resource<T> result = null;
    try {
        result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
    } finally {
        if (result == null) {
            diskCacheProvider.getDiskCache().delete(key);
        }
    }
    return result;
}

不難看出這一層是使用diskLruCache來獲取緩存的,這里先跳過,等到后面的博文在講解
如果獲取到了資源走onLoadComplete方法,如果獲取不到走onLoadFailed方法,我們看一下onLoadFailed的實(shí)現(xiàn)。

//EngineRunnable.java
private void onLoadFailed(Exception e) {
    if (isDecodingFromCache()) {
        //第一次失敗了之后會走到這里,從disk緩存獲取切換到從SOURCE獲取
        stage = Stage.SOURCE;
        //重新執(zhí)行一遍run方法
        manager.submitForSource(this);
    } else {
        manager.onException(e);
    }
}

根據(jù)注釋,可以了解到又執(zhí)行了一遍run方法,這時候就會走到decodeFromSource方法了

//EngineRunnable.java
private Resource<?> decodeFromSource() throws Exception {
    return decodeJob.decodeFromSource();
}
//DecodeJob.java
public Resource<Z> decodeFromSource() throws Exception {
    Resource<T> decoded = decodeSource();
    return transformEncodeAndTranscode(decoded);
}

private Resource<T> decodeSource() throws Exception {
    Resource<T> decoded = null;
    try {
        long startTime = LogTime.getLogTime();
        //這一步獲取到了相應(yīng)的數(shù)據(jù)
        final A data = fetcher.loadData(priority);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Fetched data", startTime);
        }
        if (isCancelled) {
            return null;
        }
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

看到這里,估計(jì)很多人都要奔潰了,這個fetcher代表什么?從哪里得到的?
我們回頭看,這個fetcher是從GenericRequest的onSizeReady中獲取出來的,而他的創(chuàng)建在更早之前,回過去找一下具體的類

//ImageVideoModelLoader
static class ImageVideoFetcher implements DataFetcher<ImageVideoWrapper> {
    private final DataFetcher<InputStream> streamFetcher;
    private final DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher;

    public ImageVideoFetcher(DataFetcher<InputStream> streamFetcher,
            DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher) {
        this.streamFetcher = streamFetcher;
        this.fileDescriptorFetcher = fileDescriptorFetcher;
    }

    @SuppressWarnings("resource")
    // @see ModelLoader.loadData
    @Override
    public ImageVideoWrapper loadData(Priority priority) throws Exception {
        InputStream is = null;
        if (streamFetcher != null) {
            try {
                is = streamFetcher.loadData(priority);
            } catch (Exception e) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
                }
                if (fileDescriptorFetcher == null) {
                    throw e;
                }
            }
        }
        ParcelFileDescriptor fileDescriptor = null;
        if (fileDescriptorFetcher != null) {
            try {
                fileDescriptor = fileDescriptorFetcher.loadData(priority);
            } catch (Exception e) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
                }
                if (is == null) {
                    throw e;
                }
            }
        }
        return new ImageVideoWrapper(is, fileDescriptor);
    }
    ...
}

這樣一來,問題又成為了streamFetcher是啥。

//ImageVideoModelLoader.java
@Override
public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
    DataFetcher<InputStream> streamFetcher = null;
    if (streamLoader != null) {
        streamFetcher = streamLoader.getResourceFetcher(model, width, height);
    }
    DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
    if (fileDescriptorLoader != null) {
        fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
    }

    if (streamFetcher != null || fileDescriptorFetcher != null) {
        return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
    } else {
        return null;
    }
}

回頭去看load的過程中創(chuàng)建的對象

//RequestManager.java
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
    ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
    ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
            Glide.buildFileDescriptorModelLoader(modelClass, context);
    if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
        throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
                + " which there is a registered ModelLoader, if you are using a custom model, you must first call"
                + " Glide#register with a ModelLoaderFactory for your custom model class");
    }

    return optionsApplier.apply(
            new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                    glide, requestTracker, lifecycle, optionsApplier));
}
//Glide.java
//傳入的class是String
public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
    return buildModelLoader(modelClass, InputStream.class, context);
}

可以看出是需要的是streamModelLoader,fileDescriptorModelLoader 這個對象,這個值獲取的較為復(fù)雜,具體的這里不講解,可以把這個方法看作是從注冊工廠中獲取出對應(yīng)的解析器即可,那么什么時候注冊的解析器的呢?答案就在第一步with中初始話Glide中完成,我這里貼一下對應(yīng)關(guān)系

   //GenericLoaderFactory注冊 
        register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
        register(File.class, InputStream.class, new StreamFileLoader.Factory());
        register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
        register(int.class, InputStream.class, new StreamResourceLoader.Factory());
        register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
        register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
        register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
        register(String.class, InputStream.class, new StreamStringLoader.Factory());
        register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
        register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
        register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
        register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
        register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());

        //TranscoderRegistry 注冊
        transcoderRegistry.register(Bitmap.class, GlideBitmapDrawable.class,
                new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool));
        transcoderRegistry.register(GifBitmapWrapper.class, GlideDrawable.class,
                new GifBitmapWrapperDrawableTranscoder(
                        new GlideBitmapDrawableTranscoder(context.getResources(), bitmapPool)));

具體舉例,loadGeneric傳入的是String,再看Glide.buildStreamModelLoader另一個參數(shù)是InputStream,那么對應(yīng)的就是
register(String.class, InputStream.class, new StreamStringLoader.Factory());
再看一下代碼

public class StreamStringLoader extends StringLoader<InputStream> implements StreamModelLoader<String> {

    /**
     * The default factory for {@link com.bumptech.glide.load.model.stream.StreamStringLoader}s.
     */
    public static class Factory implements ModelLoaderFactory<String, InputStream> {
        @Override
        public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new StreamStringLoader(factories.buildModelLoader(Uri.class, InputStream.class));
        }

        @Override
        public void teardown() {
            // Do nothing.
        }
    }

    public StreamStringLoader(Context context) {
        this(Glide.buildStreamModelLoader(Uri.class, context));
    }

    public StreamStringLoader(ModelLoader<Uri, InputStream> uriLoader) {
        super(uriLoader);
    }
}

這里也調(diào)用了buildModelLoader這個方法,那么這里就形成了一個類似遞歸的東西
分別是
register(String.class, InputStream.class, new StreamStringLoader.Factory()); register(Uri.class, InputStream.class, new StreamUriLoader.Factory()); register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
看一下HttpUrlGlideUrlLoader的代碼

//HttpUrlFetcher.java
@Override
public InputStream loadData(Priority priority) throws Exception {
    return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
        throws IOException {
    if (redirects >= MAXIMUM_REDIRECTS) {
        throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
    } else {
        // Comparing the URLs using .equals performs additional network I/O and is generally broken.
        // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
        try {
            if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
                throw new IOException("In re-direct loop");
            }
        } catch (URISyntaxException e) {
            // Do nothing, this is best effort.
        }
    }
    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(2500);
    urlConnection.setReadTimeout(2500);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);

    // Connect explicitly to avoid errors in decoders if connection fails.
    urlConnection.connect();
    if (isCancelled) {
        return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (statusCode / 100 == 2) {
        return getStreamForSuccessfulRequest(urlConnection);
    } else if (statusCode / 100 == 3) {
        String redirectUrlString = urlConnection.getHeaderField("Location");
        if (TextUtils.isEmpty(redirectUrlString)) {
            throw new IOException("Received empty or null redirect url");
        }
        URL redirectUrl = new URL(url, redirectUrlString);
        return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
    } else {
        if (statusCode == -1) {
            throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
        }
        throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
    }
}

private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
        throws IOException {
    if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
        int contentLength = urlConnection.getContentLength();
        stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
    } else {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
        }
        stream = urlConnection.getInputStream();
    }
    return stream;
}

這個方法就是真正網(wǎng)絡(luò)請求的方法了,也對結(jié)果進(jìn)行初步的處理,返回一個InputStream,那么在返回去看一下前面的邏輯,一層層的返回回去,最后到了

//DecodeJob.java
private Resource<T> decodeSource() throws Exception {
    Resource<T> decoded = null;
    try {
        long startTime = LogTime.getLogTime();
        final A data = fetcher.loadData(priority);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Fetched data", startTime);
        }
        if (isCancelled) {
            return null;
        }
        //進(jìn)行讀取到的數(shù)據(jù)的存儲及轉(zhuǎn)化
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

繼續(xù)看下去

public Resource<Z> decodeFromSource() throws Exception {
    Resource<T> decoded = decodeSource();
    return transformEncodeAndTranscode(decoded);
}
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
    long startTime = LogTime.getLogTime();
    Resource<T> transformed = transform(decoded);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transformed resource from source", startTime);
    }

    writeTransformedToCache(transformed);

    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Transcoded transformed from source", startTime);
    }
    return result;
}

private void writeTransformedToCache(Resource<T> transformed) {
    if (transformed == null || !diskCacheStrategy.cacheResult()) {
        return;
    }
    long startTime = LogTime.getLogTime();
    SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
    diskCacheProvider.getDiskCache().put(resultKey, writer);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Wrote transformed from source to cache", startTime);
    }
}

transformEncodeAndTranscode這個主要是做內(nèi)存緩存,圖片類型裝換的本文先不解析

EngineRunnable
@Override
public void run() {
    if (isCancelled) {
        return;
    }

    Exception exception = null;
    Resource<?> resource = null;
    try {
        resource = decode();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Exception decoding", e);
        }
        exception = e;
    }

    if (isCancelled) {
        if (resource != null) {
            resource.recycle();
        }
        return;
    }

    if (resource == null) {
        onLoadFailed(exception);
    } else {
        onLoadComplete(resource);
    }
}


private void onLoadComplete(Resource resource) {
    //成功時調(diào)用
    manager.onResourceReady(resource);
}

private void onLoadFailed(Exception e) {
    //失敗時調(diào)用
    if (isDecodingFromCache()) {
        stage = Stage.SOURCE;
        manager.submitForSource(this);
    } else {
        manager.onException(e);
    }
}

這個manager是在Engine中通過engineJob決定

//Engine.java
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
        transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);

//EngineRunnable
public EngineRunnable(EngineRunnableManager manager, DecodeJob<?, ?, ?> decodeJob, Priority priority) {
    this.manager = manager;
    this.decodeJob = decodeJob;
    this.stage = Stage.CACHE;
    this.priority = priority;
}

那么看一下EngineJob里面的內(nèi)容

//EngineJob.java
@Override
public void onResourceReady(final Resource<?> resource) {
    this.resource = resource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}

private static class MainThreadCallback implements Handler.Callback {
    @Override
    public boolean handleMessage(Message message) {
        if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
            EngineJob job = (EngineJob) message.obj;
            if (MSG_COMPLETE == message.what) {
                job.handleResultOnMainThread();
            } else {
                job.handleExceptionOnMainThread();
            }
            return true;
        }

        return false;
    }
}

private void handleResultOnMainThread() {
    if (isCancelled) {
        resource.recycle();
        return;
    } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
    }
    engineResource = engineResourceFactory.build(resource, isCacheable);
    hasResource = true;

    // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
    // synchronously released by one of the callbacks.
    engineResource.acquire();
    listener.onEngineJobComplete(key, engineResource);

    for (ResourceCallback cb : cbs) {
        if (!isInIgnoredCallbacks(cb)) {
            engineResource.acquire();
            cb.onResourceReady(engineResource);
        }
    }
    // Our request is complete, so we can release the resource.
    engineResource.release();
}

EngineJob里面通過handler完成了線程的切換,然后執(zhí)行handleResultOnMainThread方法,本文主要關(guān)注的是cb.onResourceReady(engineResource);
這個cb是需要追溯到GenericRequest這個類中,具體的方法是

loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
        priority, isMemoryCacheable, diskCacheStrategy, this);

可以看到他把自己作為一個callback傳了進(jìn)去,所以我們要關(guān)注的是他的相關(guān)方法

@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource) {
    if (resource == null) {
        onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
                + " inside, but instead got null."));
        return;
    }

    Object received = resource.get();
    if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
        releaseResource(resource);
        onException(new Exception("Expected to receive an object of " + transcodeClass
                + " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
                + " inside Resource{" + resource + "}."
                + (received != null ? "" : " "
                    + "To indicate failure return a null Resource object, "
                    + "rather than a Resource object containing null data.")
        ));
        return;
    }

    if (!canSetResource()) {
        releaseResource(resource);
        // We can't set the status to complete before asking canSetResource().
        status = Status.COMPLETE;
        return;
    }
    onResourceReady(resource, (R) received);
}

private void onResourceReady(Resource<?> resource, R result) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
            isFirstResource)) {
        GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
        target.onResourceReady(result, animation);
    }

    notifyLoadSuccess();

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
                + (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
    }
}

這邊邏輯就很清楚了,獲取除了資源文件在判空,然后獲取到對應(yīng)的動畫效果,并調(diào)用target.onResourceReady(result, animation);方法,這個target最早之前獲取的GlideDrawableImageViewTarget

//GlideDrawableImageViewTarget
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
    if (!resource.isAnimated()) {
        float viewRatio = view.getWidth() / (float) view.getHeight();
        float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
        if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
                && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
            resource = new SquaringDrawable(resource, view.getWidth());
        }
    }
    super.onResourceReady(resource, animation);
    this.resource = resource;
    resource.setLoopCount(maxLoopCount);
    resource.start();
}

看一下onResourceReady方法

//ImageViewTarget.java
@Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
    if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
        setResource(resource);
    }
}

protected abstract void setResource(Z resource);

setResource的具體實(shí)現(xiàn)類還是他本身

@Override
protected void setResource(GlideDrawable resource) {
    view.setImageDrawable(resource);
}

oh,終于把圖片放進(jìn)View里面,這樣一套從網(wǎng)絡(luò)加載圖片的流程也走完了!Congratulations!
當(dāng)然,由于篇幅緩存那一塊的邏輯本文暫時沒講,后續(xù)找機(jī)會補(bǔ)上。最后,老慣例總結(jié)一下內(nèi)容,into的主要流程是:

首先,先會處理ImageView的ScaleType,然后把ImageView處理成一個Target,接著看這個target是否有前一個圖片加載請求,如果有就標(biāo)記無效掉他并清空狀態(tài),如果沒有就創(chuàng)建出一個request并給這個request創(chuàng)建回調(diào)及監(jiān)聽(with中創(chuàng)建的空白的fragment),然后通過requestTracker執(zhí)行這個request。
緊接著,會測量出target的長寬并回調(diào)onSizeReady方法,在這個同時會設(shè)置占位圖。onSizeReady方法里面,除了賦值長寬之外,還會啟動Engine的load方法,在load中首先會獲取到一個EngineKey,這個key是由長,寬,地址等構(gòu)成,組件完成之后,先會查看是否有這個EngineKey的EngineJob在執(zhí)行,如果有的話,就不再請求了,采用addCallBack回調(diào)集合的方式完成圖片的下載。如果沒有的話,就通過EngineJobFactory創(chuàng)建一個新的EngineJob,同時創(chuàng)建decodeJob和EngineRunnable,然后通過EngineJob來執(zhí)行EngineRunning。
首先執(zhí)行的從disk緩存中獲取圖片,如果獲取不到,執(zhí)行從Source中獲取圖片的方法。這個方法就是請求網(wǎng)絡(luò)的方法,是由之前在load方法中創(chuàng)建的SteamFetcher來作為下載圖片的工具,這個SteamFetcher是由load的對象決定,返回的對象是InputSteam。
下載完成獲取到InputSteam之后,decodeJob把InputSteam轉(zhuǎn)化成Resource對象。接著調(diào)用EngineRunning的onLoadComplete方法,onLoadComplete會回調(diào)EngineJob的onResourceReady方法,onResourceReady會向EngineJob中的handler發(fā)送一條消息,并切換到主線程。切換到主線程之后,EngineJob又會回調(diào)之前callback集合,通知圖片已經(jīng)準(zhǔn)備完成,這個callback實(shí)現(xiàn)實(shí)在GenericRequest中。接下來,EngineJob中的callback會調(diào)用target.onResourceReady方法,這個方法最后也會調(diào)用到target.setResource方法,把獲取到的圖片設(shè)置到對應(yīng)的ImagView中。

當(dāng)然這這個總結(jié)是正對于網(wǎng)絡(luò)下載圖片的,沒涉及到緩存,估計(jì)下一篇博文講完緩存之后,會把緩存相關(guān)的補(bǔ)充到總結(jié)中。

看到這里,還不獎勵自己一下么?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容