Glide源码解析二---into方法

博客 动态
0 201
优雅殿下
优雅殿下 2022-05-12 18:59:08
悬赏:0 积分 收藏

Glide源码解析二---into方法

转载请标明出处,维权必究: https://www.cnblogs.com/tangZH/p/12543154.html

Glide作为一个强大的图片加载框架,已经被android官方使用,所以,明白Glide的加载流程以及原理对加深我们对glide的理解是很重要的。

本文基于glide 4.11

Glide.with(this).load("").into(new ImageView(this));

我们从这一句入手,上次我们看了Glide的初始化过程,也就是Glide.with(this)这个方法。现在我们来看into方法。

    @NonNull    public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {        Util.assertMainThread();        //检查view是否为null        Preconditions.checkNotNull(view);        //根据view.getScaleType()设置不同的transform变换,这个transform变换我们单独讲        BaseRequestOptions<?> requestOptions = this;        if (!requestOptions.isTransformationSet()                && requestOptions.isTransformationAllowed()                && view.getScaleType() != null) {            // Clone in this method so that if we use this RequestBuilder to load into a View and then            // into a different target, we don't retain the transformation applied based on the previous            // View's scale type.            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(                //根据transcodeClass的类型构造不同的Target                glideContext.buildImageViewTarget(view, transcodeClass),                /*targetListener=*/ null,                requestOptions,                Executors.mainThreadExecutor());    }

构建不同的target

glideContext.buildImageViewTarget(view, transcodeClass),跟着代码点进去,最后跟踪到了这里:

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

如果说我们最终要将资源解码为bitmap,那么就构造BitmapImageViewTarget,如果要将资源解码为Drawable,那么就构造DrawableImageViewTarget。

如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是DrawableImageViewTarget对象。
target里面有一些方法,比如失败的回调,设置资源等等。
 
接下来继续看代码,会调用下面这个方法。

 

    private <Y extends Target<TranscodeType>> Y into(            @NonNull Y target,            @Nullable RequestListener<TranscodeType> targetListener,            BaseRequestOptions<?> options,            Executor callbackExecutor) {        Preconditions.checkNotNull(target);        //检测是否已经调用过load方法        if (!isModelSet) {            throw new IllegalArgumentException("You must call #load() before calling #into()");        }        //构造request        Request request = buildRequest(target, targetListener, options, callbackExecutor);        //获取改target是否已经有绑定的request        Request previous = target.getRequest();        /**         * 这里修复了一个bug,详见 https://github.com/bumptech/glide/issues/2270         */        if (request.isEquivalentTo(previous)                && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {            // If the request is completed, beginning again will ensure the result is re-delivered,            // triggering RequestListeners and Targets. If the request is failed, beginning again will            // restart the request, giving it another chance to complete. If the request is already            // running, we can let it continue running without interruption.            if (!Preconditions.checkNotNull(previous).isRunning()) {                // Use the previous request rather than the new one to allow for optimizations like skipping                // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions                // that are done in the individual Request.                previous.begin();            }            return target;        }        requestManager.clear(target);        //将该request设置给target        target.setRequest(request);        requestManager.track(target, request);        return target;    }

先看一下buildRequest(target, targetListener, options, callbackExecutor);做了什么

追踪进去,调用buildRequestRecursive方法。

 

然后主要是这两个方法:

    Request mainRequest =        buildThumbnailRequestRecursive(            requestLock,            target,            targetListener,            parentCoordinator,            transitionOptions,            priority,            overrideWidth,            overrideHeight,            requestOptions,            callbackExecutor);

 

 

    Request errorRequest =        errorBuilder.buildRequestRecursive(            requestLock,            target,            targetListener,            errorRequestCoordinator,            errorBuilder.transitionOptions,            errorBuilder.getPriority(),            errorOverrideWidth,            errorOverrideHeight,            errorBuilder,            callbackExecutor);

最后设置给ErrorRequestCoordinator

errorRequestCoordinator.setRequests(mainRequest, errorRequest);

ErrorRequestCoordinator负责管理这些请求,如果请求失败就运行错误的请求。

 

我们看这个方法:buildThumbnailRequestRecursive

  private Request buildThumbnailRequestRecursive(      Object requestLock,      Target<TranscodeType> target,      RequestListener<TranscodeType> targetListener,      @Nullable RequestCoordinator parentCoordinator,      TransitionOptions<?, ? super TranscodeType> transitionOptions,      Priority priority,      int overrideWidth,      int overrideHeight,      BaseRequestOptions<?> requestOptions,      Executor callbackExecutor) {    if (thumbnailBuilder != null) {      // Recursive case: contains a potentially recursive thumbnail request builder.      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()");      }      TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =          thumbnailBuilder.transitionOptions;      // Apply our transition by default to thumbnail requests but avoid overriding custom options      // that may have been applied on the thumbnail request explicitly.      if (thumbnailBuilder.isDefaultTransitionOptionsSet) {        thumbTransitionOptions = transitionOptions;      }      Priority thumbPriority =          thumbnailBuilder.isPrioritySet()              ? thumbnailBuilder.getPriority()              : getThumbnailPriority(priority);      int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth();      int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight();      if (Util.isValidDimensions(overrideWidth, overrideHeight)          && !thumbnailBuilder.isValidOverride()) {        thumbOverrideWidth = requestOptions.getOverrideWidth();        thumbOverrideHeight = requestOptions.getOverrideHeight();      }      ThumbnailRequestCoordinator coordinator =          new ThumbnailRequestCoordinator(requestLock, parentCoordinator);      Request fullRequest =          obtainRequest(              requestLock,              target,              targetListener,              requestOptions,              coordinator,              transitionOptions,              priority,              overrideWidth,              overrideHeight,              callbackExecutor);      isThumbnailBuilt = true;      // Recursively generate thumbnail requests.      Request thumbRequest =          thumbnailBuilder.buildRequestRecursive(              requestLock,              target,              targetListener,              coordinator,              thumbTransitionOptions,              thumbPriority,              thumbOverrideWidth,              thumbOverrideHeight,              thumbnailBuilder,              callbackExecutor);      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(requestLock, parentCoordinator);      Request fullRequest =          obtainRequest(              requestLock,              target,              targetListener,              requestOptions,              coordinator,              transitionOptions,              priority,              overrideWidth,              overrideHeight,              callbackExecutor);      BaseRequestOptions<?> thumbnailOptions =          requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);      Request thumbnailRequest =          obtainRequest(              requestLock,              target,              targetListener,              thumbnailOptions,              coordinator,              transitionOptions,              getThumbnailPriority(priority),              overrideWidth,              overrideHeight,              callbackExecutor);      coordinator.setRequests(fullRequest, thumbnailRequest);      return coordinator;    } else {      // Base case: no thumbnail.      return obtainRequest(          requestLock,          target,          targetListener,          requestOptions,          parentCoordinator,          transitionOptions,          priority,          overrideWidth,          overrideHeight,          callbackExecutor);    }  }

首先对缩略图及是否对Target设置参数的判断(是否使用了thumbnail()方法和sizeMultiplier()方法),如果有使用thunmnail()方法,则生成原始图片和缩略图的请求,并由ThumbnailRequestCoordinator对象来协调管理,使用了sizeMultiplier()方法,则同样的处理(前者递归的获得缩略图的Request,后者不递归),否则就只生成原始图片的请求。

 

他们最终都会调用obtainRequest方法,追踪进去可以发现该方法最终返回的是SingleRequest对象。初始化request的时候传递的参数很多:

  public static <R> SingleRequest<R> obtain(      Context context,      GlideContext glideContext,      Object requestLock,      Object model,      Class<R> transcodeClass,      BaseRequestOptions<?> requestOptions,      int overrideWidth,      int overrideHeight,      Priority priority,      Target<R> target,      RequestListener<R> targetListener,      @Nullable List<RequestListener<R>> requestListeners,      RequestCoordinator requestCoordinator,      Engine engine,      TransitionFactory<? super R> animationFactory,      Executor callbackExecutor) {

 

1.GlideContext glideContext : 全局上下文
2.Object model :加载的资源类型
3.Class transcodeClass :转换的类型
4.RequestOptions requestOptions:设置选项(包括:skipMemoryCache,errorDrawable,placeholder,timeoutOf,encodeFormatOf等等)
5.int overrideWidth:目标宽度在所需资源的像素点。
6.int overrideHeight:目标高度在所需资源的像素点。
7. Priority priority:加载的优先级(IMMEDIATE,HIGH,NORMAL,LOW)
8.Target target:上面刚讲过,绑定的target
9.RequestListener requestListener:请求加载时候的监听器
10.RequestCoordinator requestCoordinator:请求协调器(用来协调具有相同Target的协调器)
11.Engine engine:负责启动负载和管理活动和缓存资源。
12.TransitionFactory<? super R> animationFactory:一个工厂类,可以根据请求的状态产生不同的转换。

 

我们再回到into代码中,获取了request之后我们就要开始请求了。

我们看着一句requestManager.track(target, request);

  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {    targetTracker.track(target);    requestTracker.runRequest(request);  }

runRequest就是执行请求的代码:

  /** Starts tracking the given request. */  public void runRequest(@NonNull Request request) {    requests.add(request);    if (!isPaused) {      request.begin();    } else {      request.clear();      if (Log.isLoggable(TAG, Log.VERBOSE)) {        Log.v(TAG, "Paused, delaying request");      }      pendingRequests.add(request);    }  }

判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request,否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。

 

我们来看begin方法:

  @Override  public void begin() {    synchronized (requestLock) {      assertNotCallingCallbacks();      stateVerifier.throwIfRecycled();      startTime = LogTime.getLogTime();      if (model == null) {        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {          width = overrideWidth;          height = overrideHeight;        }        // Only log at more verbose log levels if the user has set a fallback drawable, because        // fallback Drawables indicate the user expects null models occasionally.        int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;        onLoadFailed(new GlideException("Received null model"), logLevel);        return;      }      if (status == Status.RUNNING) {        throw new IllegalArgumentException("Cannot restart a running request");      }
如果说这个资源已经被加载过了,那么我们直接调用onResourceReady
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged // that starts an identical request into the same Target or View), we can simply use the // resource and size we retrieved the last time around and skip obtaining a new size, starting // a new load etc. This does mean that users who want to restart a load because they expect // that the view size has changed will need to explicitly clear the View or Target before // starting the new load. if (status == Status.COMPLETE) { onResourceReady(resource, DataSource.MEMORY_CACHE); return; } // Restarts for requests that are neither complete nor running can be treated as new requests // and can run again from the beginning. status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); } if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (IS_VERBOSE_LOGGABLE) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } } }

如果model为null,说明我们没有调用load方法,这时候会回调onLoadFailed,将status设置为Status.FAILED,然后调用setErrorPlaceholder,这个方法里面最终调用target.onLoadFailed(error);将资源置空,然后显示错误图片。

 

    @Override    public void begin() {        synchronized (requestLock) {            assertNotCallingCallbacks();            stateVerifier.throwIfRecycled();            startTime = LogTime.getLogTime();            if (model == null) {                if (Util.isValidDimensions(overrideWidth, overrideHeight)) {                    width = overrideWidth;                    height = overrideHeight;                }                // Only log at more verbose log levels if the user has set a fallback drawable, because                // fallback Drawables indicate the user expects null models occasionally.                int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;                onLoadFailed(new GlideException("Received null model"), logLevel);                return;            }            if (status == SingleRequest.Status.RUNNING) {                throw new IllegalArgumentException("Cannot restart a running request");            }            /**             * 如果完成后重新启动(通常是通过notifyDataSetChanged之类的方法             * 将相同的请求发送到相同的Target或View),我们可以简单地使用             * 我们最后一次检索的资源和大小,然后跳过获取新大小的步骤,             * 不用开始一个新的加载。这确实意味着要重新加载的用户,因为他们             * 更改视图大小,那么需要先明确清除view和target,然后             * 开始新的加载。             */            if (status == SingleRequest.Status.COMPLETE) {                onResourceReady(resource, DataSource.MEMORY_CACHE);                return;            }            // Restarts for requests that are neither complete nor running can be treated as new requests            // and can run again from the beginning.            status = SingleRequest.Status.WAITING_FOR_SIZE;            /**             * 这里会判断Util.isValidDimensions(overrideWidth, overrideHeight)             * 如果你在使用时候调用了override() API为图片指定了一个固定的宽高,就会按照你给定的去加载;第二种情况是没有给定的情况,             * 那么target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高,             * 具体计算就在getSize里面             * 但是不管怎样,最后都会调用onSizeReady()。             */            if (Util.isValidDimensions(overrideWidth, overrideHeight)) {                onSizeReady(overrideWidth, overrideHeight);            } else {                target.getSize(this);            }            if ((status == SingleRequest.Status.RUNNING || status == SingleRequest.Status.WAITING_FOR_SIZE)                    && canNotifyStatusChanged()) {                target.onLoadStarted(getPlaceholderDrawable());            }            if (IS_VERBOSE_LOGGABLE) {                logV("finished run method in " + LogTime.getElapsedMillis(startTime));            }        }    }

我们进去onSizeReady看看

    @Override    public void onSizeReady(int width, int height) {        //如果对象以及被回收了,那么抛出异常        stateVerifier.throwIfRecycled();        synchronized (requestLock) {            if (IS_VERBOSE_LOGGABLE) {                logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));            }            //说明没有设置大小或者没有获取到计算后的大小            if (status != SingleRequest.Status.WAITING_FOR_SIZE) {                return;            }            status = SingleRequest.Status.RUNNING;            float sizeMultiplier = requestOptions.getSizeMultiplier();            this.width = maybeApplySizeMultiplier(width, sizeMultiplier);            this.height = maybeApplySizeMultiplier(height, sizeMultiplier);            if (IS_VERBOSE_LOGGABLE) {                logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));            }            loadStatus =                    engine.load(                            glideContext,                            model,                            requestOptions.getSignature(),                            this.width,                            this.height,                            requestOptions.getResourceClass(),                            transcodeClass,                            priority,                            requestOptions.getDiskCacheStrategy(),                            requestOptions.getTransformations(),                            requestOptions.isTransformationRequired(),                            requestOptions.isScaleOnlyOrNoTransform(),                            requestOptions.getOptions(),                            requestOptions.isMemoryCacheable(),                            requestOptions.getUseUnlimitedSourceGeneratorsPool(),                            requestOptions.getUseAnimationPool(),                            requestOptions.getOnlyRetrieveFromCache(),                            this,                            callbackExecutor);            // This is a hack that's only useful for testing right now where loads complete synchronously            // even though under any executor running on any thread but the main thread, the load would            // have completed asynchronously.            if (status != SingleRequest.Status.RUNNING) {                loadStatus = null;            }            if (IS_VERBOSE_LOGGABLE) {                logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));            }        }    }

主要的代码是engine.load。

    /**     * /**     *  所有的请求流程都如下:     *  1.检查内存缓存并提供缓存的资源     *  2.检查当前使用的资源,并返回当前的活跃资源     *  3.检查当前的加载进度,并将cb添加到正在进行的加载进度中     *  4.开始一个新的加载     *     * @param glideContext     * @param model     * @param signature     * @param width     * @param height     * @param resourceClass     * @param transcodeClass     * @param priority     * @param diskCacheStrategy     * @param transformations     * @param isTransformationRequired     * @param isScaleOnlyOrNoTransform     * @param options     * @param isMemoryCacheable     * @param useUnlimitedSourceExecutorPool     * @param useAnimationPool     * @param onlyRetrieveFromCache     * @param cb     * @param callbackExecutor     * @param <R>     * @return     */    public <R> Engine.LoadStatus load(            GlideContext glideContext,            Object model,            Key signature,            int width,            int height,            Class<?> resourceClass,            Class<R> transcodeClass,            Priority priority,            DiskCacheStrategy diskCacheStrategy,            Map<Class<?>, Transformation<?>> transformations,            boolean isTransformationRequired,            boolean isScaleOnlyOrNoTransform,            Options options,            boolean isMemoryCacheable,            boolean useUnlimitedSourceExecutorPool,            boolean useAnimationPool,            boolean onlyRetrieveFromCache,            ResourceCallback cb,            Executor callbackExecutor) {        long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;        //构造一个key        EngineKey key =                keyFactory.buildKey(                        model,                        signature,                        width,                        height,                        transformations,                        resourceClass,                        transcodeClass,                        options);        EngineResource<?> memoryResource;        synchronized (this) {            //通过这个key去缓存中看是不是存在资源,loadFromMemory里面会先去活跃资源缓存池中获取,            // 没有的话再去内存缓存中获取,活跃资源即现在正在被其他组件使用的资源。            memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);            if (memoryResource == null) {                return waitForExistingOrStartNewJob(                        glideContext,                        model,                        signature,                        width,                        height,                        resourceClass,                        transcodeClass,                        priority,                        diskCacheStrategy,                        transformations,                        isTransformationRequired,                        isScaleOnlyOrNoTransform,                        options,                        isMemoryCacheable,                        useUnlimitedSourceExecutorPool,                        useAnimationPool,                        onlyRetrieveFromCache,                        cb,                        callbackExecutor,                        key,                        startTime);            }        }        // Avoid calling back while holding the engine lock, doing so makes it easier for callers to        // deadlock.        cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);        return null;    }

 如果找得到资源,那么就回调cb.onResourceReady,不然的话会走waitForExistingOrStartNewJob。

我们进去看一下:

    private <R> Engine.LoadStatus waitForExistingOrStartNewJob(            GlideContext glideContext,            Object model,            Key signature,            int width,            int height,            Class<?> resourceClass,            Class<R> transcodeClass,            Priority priority,            DiskCacheStrategy diskCacheStrategy,            Map<Class<?>, Transformation<?>> transformations,            boolean isTransformationRequired,            boolean isScaleOnlyOrNoTransform,            Options options,            boolean isMemoryCacheable,            boolean useUnlimitedSourceExecutorPool,            boolean useAnimationPool,            boolean onlyRetrieveFromCache,            ResourceCallback cb,            Executor callbackExecutor,            EngineKey key,            long startTime) {        //通过key获取EngineJob,EngineJob负责开启线程异步加载。        EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);        if (current != null) {            current.addCallback(cb, callbackExecutor);            if (VERBOSE_IS_LOGGABLE) {                logWithTimeAndKey("Added to existing load", startTime, key);            }            return new Engine.LoadStatus(cb, current);        }        //没有EngineJob则构建一个        EngineJob<R> engineJob =                engineJobFactory.build(                        key,                        isMemoryCacheable,                        useUnlimitedSourceExecutorPool,                        useAnimationPool,                        onlyRetrieveFromCache);        //负责给图片解码等一些复杂操作        DecodeJob<R> decodeJob =                decodeJobFactory.build(                        glideContext,                        model,                        key,                        signature,                        width,                        height,                        resourceClass,                        transcodeClass,                        priority,                        diskCacheStrategy,                        transformations,                        isTransformationRequired,                        isScaleOnlyOrNoTransform,                        onlyRetrieveFromCache,                        options,                        engineJob);        jobs.put(key, engineJob);        engineJob.addCallback(cb, callbackExecutor);        //运行        engineJob.start(decodeJob);        if (VERBOSE_IS_LOGGABLE) {            logWithTimeAndKey("Started new load", startTime, key);        }        return new Engine.LoadStatus(cb, engineJob);    }

 

 

  public synchronized void start(DecodeJob<R> decodeJob) {    this.decodeJob = decodeJob;    GlideExecutor executor =        decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();    executor.execute(decodeJob);  }

如果要从磁盘缓存中去解码的话,就获取diskCacheExecutor,否则就用针对原始资源的一个执行器。

在executor.execute(decodeJob)之后便切换到子线程了,我们到DecodeJob里面去看一下。

 

  @Override  public void run() {    // This should be much more fine grained, but since Java's thread pool implementation silently    // swallows all otherwise fatal exceptions, this will at least make it obvious to developers    // that something is failing.    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);    // Methods in the try statement can invalidate currentFetcher, so set a local variable here to    // ensure that the fetcher is cleaned up either way.    DataFetcher<?> localFetcher = currentFetcher;    try {      if (isCancelled) {        notifyFailed();        return;      }      runWrapped();    } catch (CallbackException e) {      // If a callback not controlled by Glide throws an exception, we should avoid the Glide      // specific debug logic below.      throw e;    } catch (Throwable t) {      // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our      // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We      // are however ensuring that our callbacks are always notified when a load fails. Without this      // notification, uncaught throwables never notify the corresponding callbacks, which can cause      // loads to silently hang forever, a case that's especially bad for users using Futures on      // background threads.      if (Log.isLoggable(TAG, Log.DEBUG)) {        Log.d(            TAG,            "DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage,            t);      }      // When we're encoding we've already notified our callback and it isn't safe to do so again.      if (stage != Stage.ENCODE) {        throwables.add(t);        notifyFailed();      }      if (!isCancelled) {        throw t;      }      throw t;    } finally {      // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call      // close in all cases anyway.      if (localFetcher != null) {        localFetcher.cleanup();      }      GlideTrace.endSection();    }  }

 

 主要是runWrapped();

 

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

 

当INITIALIZE或者SWITCH_TO_SOURCE_SERVICE的时候,走runGenerators()。这两种是没有缓存的情况下。

runGenerators():

  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;      }    }    // We've run out of stages and generators, give up.    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {      notifyFailed();    }    // Otherwise a generator started a new load and we expect to be called back in    // onDataFetcherReady.  }

 

重点:currentGenerator.startNext()。实现startNext方法的有三个:

 

 而我们的currentGenerator是哪一个呢?

 

回头看runWrapped

case INITIALIZE:        stage = getNextStage(Stage.INITIALIZE);        currentGenerator = getNextGenerator();        runGenerators();

点进去getNextGenerator,结合status的值便可以知道返回的是SourceGenerator。(我们讨论的是初次加载没有缓存的情况)

我们来到SourceGenerator的startNext()方法:

 

 

    @Override    public boolean startNext() {        if (dataToCache != null) {            Object data = dataToCache;            dataToCache = null;            cacheData(data);        }        if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {            return true;        }        sourceCacheGenerator = null;        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;    }

如果sourceCacheGenerator 不为null,就调用它的startNext,在里面去获取modelLoader,然后去加载资源,modelLoader即模型加载器,Glide初始化的时候注册了很多模型加载器。

registry        .append(int.class, InputStream.class, resourceLoaderStreamFactory)        .append(int.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)        .append(Integer.class, InputStream.class, resourceLoaderStreamFactory)        .append(Integer.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)        .append(Integer.class, Uri.class, resourceLoaderUriFactory)        .append(int.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)        .append(Integer.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)        .append(int.class, Uri.class, resourceLoaderUriFactory)        .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())        .append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())

如append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())

将Uri对象转换为InputStream,模型加载器为DataUrlLoader.StreamFactory,也就是说我们加载的时候如果传进来的是一个uri对象,那么最终会被转换为InputStream。

我们不进去看,直接看接下来的代码。

loadData = helper.getLoadData().get(loadDataListIndex++);

loadData里面包含着:

sourceKey:标识这个加载的原始资源的key

alternateKeys:备用的缓存key指向相同的数据

DataFetcher:用来获取没有在缓存中发现的数据(即需要去加载的,modelLoader中都包含着这个)

 

接下来看:

startNextLoad(loadData);

private void startNextLoad(final LoadData<?> toStart) {  loadData.fetcher.loadData(      helper.getPriority(),      new DataCallback<Object>() {        @Override        public void onDataReady(@Nullable Object data) {          if (isCurrentRequest(toStart)) {            onDataReadyInternal(toStart, data);          }        }        @Override        public void onLoadFailed(@NonNull Exception e) {          if (isCurrentRequest(toStart)) {            onLoadFailedInternal(toStart, e);          }        }      });}

loadData.fetcher.loadData:这里是我们真正去加载资源的地方。

点击进去loadeData,发现好多实现了该方法的类。那我们这里的Fetcher究竟是哪一个呢?

首先从名字来看,如果我们加载的是网络资源,那么就是:HttpUrlFetcher。

 

这个HttpUrlFetcher跟我们的modelLoader是什么关系呢

我们可以看出LoadData在ModelLoader类中。

 查看HttpUrlFetcher的调用可以追溯到HttpGlideUrlLoader。

 

 

  @Override  public LoadData<InputStream> buildLoadData(      @NonNull GlideUrl model, int width, int height, @NonNull Options options) {    // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time    // spent parsing urls.    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));  }

我们可以看出HttpGlideUrlLoader实现了ModelLoader的方法,buildLoadData,而在buildLoadData中返回了一个LoadData对象,这个对象传入的就是HttpUrlFetcher。

这个buildLoadData什么时候被调用的呢?

我们回到startNext方法,loadData = helper.getLoadData().get(loadDataListIndex++);的getLoadData()里面:

 

 好,那么我们看HttpUrlFetcher的loadData();

  @Override  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) {      if (Log.isLoggable(TAG, Log.DEBUG)) {        Log.d(TAG, "Failed to load data for url", e);      }      callback.onLoadFailed(e);    } finally {      if (Log.isLoggable(TAG, Log.VERBOSE)) {        Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));      }    }  }

loadDataWithRedirects返回了InputStream,然后callback回调。

我们进去loadDataWithRedirects看可以发现这个方法其实是去请求网络,我们就不细看了。

  private void startNextLoad(final LoadData<?> toStart) {    loadData.fetcher.loadData(        helper.getPriority(),        new DataCallback<Object>() {          @Override          public void onDataReady(@Nullable Object data) {            if (isCurrentRequest(toStart)) {              onDataReadyInternal(toStart, data);            }          }          @Override          public void onLoadFailed(@NonNull Exception e) {            if (isCurrentRequest(toStart)) {              onLoadFailedInternal(toStart, e);            }          }        });  }

回调后调用onDataReadyInternal(toStart, data);

且看一下:

@SuppressWarnings("WeakerAccess")@Syntheticvoid onDataReadyInternal(LoadData<?> loadData, Object data) {  DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();  if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {    dataToCache = data;    // We might be being called back on someone else's thread. Before doing anything, we should    // reschedule to get back onto Glide's thread.    cb.reschedule();  } else {    cb.onDataFetcherReady(        loadData.sourceKey,        data,        loadData.fetcher,        loadData.fetcher.getDataSource(),        originalKey);  }}

我们看这个方法:onDataFetcherReady

查看一下便可以知道会回调DecodeJob中的方法:

  @Override  public void onDataFetcherReady(      Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {    this.currentSourceKey = sourceKey;    this.currentData = data;    this.currentFetcher = fetcher;    this.currentDataSource = dataSource;    this.currentAttemptingKey = attemptedKey;    if (Thread.currentThread() != currentThread) {      runReason = RunReason.DECODE_DATA;      callback.reschedule(this);    } else {      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");      try {        decodeFromRetrievedData();      } finally {        GlideTrace.endSection();      }    }  }

到此我们就完成了加网络资源的过程,接下来就是解码等等的操作了。

我们看decodeFromRetrievedData:

里面有一句:

resource = decodeFromData(currentFetcher, currentData, currentDataSource);

将资源解码成Resource。

我们追踪进去:

decodeFromData -> decodeFromFetcher -> runLoadPath -> path.load -> loadWithExceptionList

loadWithExceptionList里面便开始进行我们的解码操作了。

 

  private Resource<Transcode> loadWithExceptionList(      DataRewinder<Data> rewinder,      @NonNull Options options,      int width,      int height,      DecodePath.DecodeCallback<ResourceType> decodeCallback,      List<Throwable> exceptions)      throws GlideException {    Resource<Transcode> result = null;    //noinspection ForLoopReplaceableByForEach to improve perf    for (int i = 0, size = decodePaths.size(); i < size; i++) {      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);      try {        result = path.decode(rewinder, width, height, options, decodeCallback);      } catch (GlideException e) {        exceptions.add(e);      }      if (result != null) {        break;      }    }

我们看:

result = path.decode(rewinder, width, height, options, decodeCallback);

  public Resource<Transcode> decode(      DataRewinder<DataType> rewinder,      int width,      int height,      @NonNull Options options,      DecodeCallback<ResourceType> callback)      throws GlideException {    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);    return transcoder.transcode(transformed, options);  }

我们看decodeResource -> decodeResourceWithList

   @NonNull    private Resource<ResourceType> decodeResourceWithList(            DataRewinder<DataType> rewinder,            int width,            int height,            @NonNull Options options,            List<Throwable> exceptions)            throws GlideException {        Resource<ResourceType> result = null;        //noinspection ForLoopReplaceableByForEach to improve perf        //遍历获取解码器        for (int i = 0, size = decoders.size(); i < size; i++) {            ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);            try {                DataType data = rewinder.rewindAndGet();                //该解码器是否可以解码该数据                if (decoder.handles(data, options)) {                    //获取数据                    data = rewinder.rewindAndGet();                    //解码                    result = decoder.decode(data, width, height, options);                }                // Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but                // instead log and continue. See #2406 for an example.            } catch (IOException | RuntimeException | OutOfMemoryError e) {                if (Log.isLoggable(TAG, Log.VERBOSE)) {                    Log.v(TAG, "Failed to decode data for " + decoder, e);                }                exceptions.add(e);            }            if (result != null) {                break;            }        }        if (result == null) {            throw new GlideException(failureMessage, new ArrayList<>(exceptions));        }        return result;    }

 

获取对应的解码器后用decode方法进行解码。Glide初始化的时候便注册了一大堆解码器,如:

.append(            Registry.BUCKET_GIF,            InputStream.class,            GifDrawable.class,            new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))

对于Gif类型,将InputStream解码为GifDrawable,解码器为StreamGifDecoder

接下来我们回到这个方法:

  public Resource<Transcode> decode(      DataRewinder<DataType> rewinder,      int width,      int height,      @NonNull Options options,      DecodeCallback<ResourceType> callback)      throws GlideException {    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);    return transcoder.transcode(transformed, options);  }

Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);

这个方法里面便是对我们的资源进行了变换。

往下追溯到这个方法:

  @Synthetic  @NonNull  <Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {    @SuppressWarnings("unchecked")    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();    Transformation<Z> appliedTransformation = null;    Resource<Z> transformed = decoded;    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);      transformed = appliedTransformation.transform(glideContext, decoded, width, height);    }    // TODO: Make this the responsibility of the Transformation.    if (!decoded.equals(transformed)) {      decoded.recycle();    }    final EncodeStrategy encodeStrategy;    final ResourceEncoder<Z> encoder;    if (decodeHelper.isResourceEncoderAvailable(transformed)) {      encoder = decodeHelper.getResultEncoder(transformed);      encodeStrategy = encoder.getEncodeStrategy(options);    } else {      encoder = null;      encodeStrategy = EncodeStrategy.NONE;    }    Resource<Z> result = transformed;    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);    if (diskCacheStrategy.isResourceCacheable(        isFromAlternateCacheKey, dataSource, encodeStrategy)) {      if (encoder == null) {        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());      }      final Key key;      switch (encodeStrategy) {        case SOURCE:          key = new DataCacheKey(currentSourceKey, signature);          break;        case TRANSFORMED:          key =              new ResourceCacheKey(                  decodeHelper.getArrayPool(),                  currentSourceKey,                  signature,                  width,                  height,                  appliedTransformation,                  resourceSubClass,                  options);          break;        default:          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);      }      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);      deferredEncodeManager.init(key, encoder, lockedResult);      result = lockedResult;    }    return result;  }

 首先获取Transformation,然后调用transform方法进行变换处理。

 

返回resource之后回到decodeFromRetrievedData方法。

  private void decodeFromRetrievedData() {    if (Log.isLoggable(TAG, Log.VERBOSE)) {      logWithTimeAndKey(          "Retrieved data",          startFetchTime,          "data: "              + currentData              + ", cache key: "              + currentSourceKey              + ", fetcher: "              + currentFetcher);    }    Resource<R> resource = null;    try {      resource = decodeFromData(currentFetcher, currentData, currentDataSource);    } catch (GlideException e) {      e.setLoggingDetails(currentAttemptingKey, currentDataSource);      throwables.add(e);    }    if (resource != null) {      notifyEncodeAndRelease(resource, currentDataSource);    } else {      runGenerators();    }  }

 

我们沿着方法进入notifyEncodeAndRelease ---> notifyComplete ----> onResourceReady ------>  notifyCallbacksOfResult

 

。。。。算了写的到这里好累,接下来不写了自己看,就是去设置资源。

 

 

 

 

 

posted @ 2022-05-12 17:57 妖久 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员