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

来源:https://www.cnblogs.com/tangZH/archive/2022/05/12/12543154.html
-Advertisement-
Play Games

轉載請標明出處,維權必究: https://www.cnblogs.com/tangZH/p/12543154.html Glide作為一個強大的圖片載入框架,已經被android官方使用,所以,明白Glide的載入流程以及原理對加深我們對glide的理解是很重要的。 本文基於glide 4.11 ...


轉載請標明出處,維權必究: 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;
      }
    }
   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文例子參考《STM32單片機開發實例——基於Proteus虛擬模擬與HAL/LL庫》 源代碼:https://github.com/LanLinnet/STM33F103R6 項目要求 實現呼吸燈的效果:D1為長亮LED,D2為呼吸燈,通過PWM的方式實現D2亮→滅→亮→滅……的漸變效果,一次變化 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 前情提要: 該方法只適用於Windows10以上版本,Ubuntu直接可在微軟商店安裝 在這個網頁docs.microsoft.com/zh-cn/windo… 可以設置完成前5部,然後根據下麵具體操作。 ps:1.在連接中我們要打開powers ...
  • 五一5天小假期的結束大家休息好了嗎?上班了狀態回整的怎麼樣呢?阿裡云云發平臺給大家帶福利了呢,通過玩游戲把獎品帶回家。 雲開發平臺兩周年,0門檻部署上線4款熱門游戲,游戲暢玩還有AirPods耳機、筋膜槍等8種獎品任你挑啦!4款游戲只需要完成2個就可以抽獎呢,100%中獎;還額外有故事有獎徵集的板塊 ...
  • 一、引言 SQL Server有一些很好用的功能,只不過由於個人原因沒用過或者不記得怎麼用,導致需要花點時間用其它方式來實現。 二、好用小知識 2.1、FORMAT函數 1)時間格式化,如將當前日期格式化為2022-05-12: SELECT FORMAT(GETDATE(),'yyyy-MM-dd ...
  • 一、引言 A表數據同步至B表的場景很常見,比如一個公司有總部及分廠,它們使用相同的系統,只是賬套不同。此時,一些基礎數據如物料信息,只需要總部錄入即可,然後間隔一定時間同步至分廠,避免了重覆工作。 二、測試數據 CREATE TABLE StudentA ( ID VARCHAR(32), Name ...
  • 本文介紹什麼是 SQL 的聚集函數,如何利用它們彙總表的數據。這些函數很高效,它們返回結果一般比你在自己的客戶端應用程式中計算要快得多。 一、聚集函數 我們經常需要彙總數據而不用把它們實際檢索出來,為此 SQL 提供了專門的函數。使用這些函數,SQL 查詢可用於檢索數據,以便分析和報表生成。這種類型 ...
  • 本文介紹什麼是函數,DBMS 支持何種函數,以及如何使用這些函數;還將講解為什麼 SQL 函數的使用可能會帶來問題。 一、函數 與大多數其他電腦語言一樣,SQL 也可以用函數來處理數據。函數一般是在數據上執行的,為數據的轉換和處理提供了方便。 SQL 如何創建計算欄位 中用來去掉字元串尾的空格的 ...
  • 本文介紹什麼是計算欄位,如何創建計算欄位,我們用例子說明瞭計算欄位在字元串拼接和算術計算中的用途。以及如何從應用程式中使用別名引用它們。 一、計算欄位 存儲在資料庫表中的數據一般不是應用程式所需要的格式,下麵舉幾個例子。 需要顯示公司名,同時還需要顯示公司的地址,但這兩個信息存儲在不同的表列中。 城 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...