中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android圖片框架Glide原理是什么

發布時間:2022-11-07 10:23:27 來源:億速云 閱讀:114 作者:iii 欄目:開發技術

本篇內容主要講解“Android圖片框架Glide原理是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Android圖片框架Glide原理是什么”吧!

首先引入依賴

    implementation 'com.github.bumptech.glide:glide:4.12.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

下面一行代碼,就是Glide最簡單的使用方式了

Glide.with(this).load(url).into(imageView)

with

首先,我們來看with,其實with的功能就是根據傳入的context來獲取圖片請求管理器RequestManager,用來啟動和管理圖片請求。

  public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }

context可以傳入app,activity和fragment,這關系著圖片請求的生命周期。通常使用當前頁面的context,這樣當我們打開一個頁面加載圖片,然后退出頁面時,圖片請求會跟隨頁面銷毀而被取消,而不是繼續加載浪費資源。

當context是app時,獲得的RequestManager是一個全局單例,圖片請求的生命周期會跟隨整個app。

注意:如果with發生在子線程,不管context是誰,都返回應用級別的RequestManager單例。

  private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          // Normally pause/resume is taken care of by the fragment we add to the fragment or
          // activity. However, in this case since the manager attached to the application will not
          // receive lifecycle events, we must force the manager to start resumed using
          // ApplicationLifecycle.
          // TODO(b/27524013): Factor out this Glide.get() call.
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }
    return applicationManager;
  }

當context是Activity時,會創建一個無界面的fragment添加到Activity,用于感知Activity的生命周期,同時創建RequestManager給該fragment持有。

  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      // This is a bit of hack, we're going to start the RequestManager, but not the
      // corresponding Lifecycle. It's safe to start the RequestManager, but starting the
      // Lifecycle might trigger memory leaks. See b/154405040
      if (isParentVisible) {
        requestManager.onStart();
      }
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

load

load方法會得到一個圖片請求構建器RequestBuilder,用來創建圖片請求。

  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

into

首先是根據ImageView的ScaleType,來配置參數

  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    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(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

繼續跟進into,會創建圖片請求,獲取Target載體已有的請求,對比兩個請求,如果等效,啟動異步請求。然后,圖片載體綁定圖片請求,也就是imageView setTag為request

  private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    Request previous = target.getRequest();
    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);
    target.setRequest(request);
    requestManager.track(target, request);
    return target;
  }

繼續跟進異步請求 requestManager.track(target, request)

  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();//開啟圖片請求
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);//如果是暫停狀態,就把請求存起來
    }
  }

到這里就啟動了圖片請求了,我們繼續跟進request.begin()

  public void begin() {
    synchronized (requestLock) {
      //......
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      //如果有尺寸,開始加載
        onSizeReady(overrideWidth, overrideHeight);
      } else {
      //如果無尺寸就先去獲取
        target.getSize(this);
      }
      //......
    }
  }

然后繼續瞧瞧onSizeReady

  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
    //......
      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);
      //......
    }
  }

跟進engine.load

  public <R> 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;
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);
    EngineResource<?> memoryResource;
    synchronized (this) {
      //從內存加載
      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);
      }
    }
    cb.onResourceReady(
        memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
    return null;
  }
  private <R> 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) {
    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 LoadStatus(cb, current);
    }
    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 LoadStatus(cb, engineJob);
  }

DecodeJob是一個Runnable,它通過一系列的調用,會來到HttpUrlFetcher的loadData方法

  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      //獲取輸入流,此處使用的是HttpURLConnection
      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));
      }
    }
  }

至此,網絡請求結束,最后把圖片設置上去就行了,在SingleRequest的onResourceReady方法,它會把結果回調給Target載體

target.onResourceReady(result, animation);

繼續跟進它,最終會執行setResource,把圖片設置上去

  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

原理總結

with根據傳入的context來獲取圖片請求管理器RequestManager,當傳入的context是App時,獲得的RequestManager是一個全局單例,圖片請求的生命周期會跟隨這個應用,當傳入的是Activity時,會創建一個無界面的空fragment添加到Activity,用來感知Activity的生命周期。load會得到了一個圖片請求構建器RequestBuilder,用來創建圖片請求。into開啟加載,先會根據ImageView的ScaleType來配置參數,創建圖片請求,圖片載體綁定圖片請求,然后開啟圖片請求,先從內存中加載,如果內存里沒有,會創建一個Runnable,通過一系列的調用,使用HttpURLConnection獲取網絡輸入流,把結果回調出去,最后把回調結果設置上去就OK了。

緩存

Glide三級緩存原理:讀取一張圖片時,順序是: 弱引用緩存,LruCache,磁盤緩存。

用Glide加載某張圖片時,先去弱引用緩存中尋找圖片,如果有則直接取出來使用,如果沒有,則去LruCache中尋找,如果LruCache中有,則從中取出圖片使用,并將它放入弱引用緩存中,如果都沒有圖片,則從磁盤緩存或網絡中加載圖片。

  private EngineResource<?> loadFromMemory(
      EngineKey key, boolean isMemoryCacheable, long startTime) {
    if (!isMemoryCacheable) {
      return null;
    }
    EngineResource<?> active = loadFromActiveResources(key); //從弱引用獲取圖片
    if (active != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return active;
    }
    EngineResource<?> cached = loadFromCache(key); //從LruCache獲取緩存圖片
    if (cached != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return cached;
    }
    return null;
  }

不過,這也會產生一個問題,就是Glide加載圖片時,URL不變但是圖片變了,這種情況會還是以前的舊圖片。因為Glide加載圖片會將圖片緩存到本地,如果url不變則直接讀取緩存不會再網絡加載。

解決方案:

清除緩存讓后臺每次都更改圖片的名字圖片地址選用 ”url?key="+隨機數這種格式

LruCache

LruCache就是維護一個緩存對象列表,其中對象列表的排列方式是按照訪問順序實現的,即一直沒訪問的對象,將放在隊尾,即將被淘汰。而最近訪問的對象將放在隊頭,最后被淘汰。其內部維護了一個集合LinkedHashMap,LinkHashMap繼承HashMap,在HashMap的基礎上,新增了雙向鏈表結構,每次訪問數據的時候,會更新被訪問數據的鏈表指針,該LinkedHashMap是以訪問順序排序的,當調用put()方法時,就會在集合中添加元素,判斷緩存是否已滿,如果滿了就用LinkedHashMap的迭代器刪除隊尾元素,即近期最少訪問的元素。當調用get()方法訪問緩存對象時,就會調用LinkedHashMap的get()方法獲得對應集合元素,同時會更新該元素到隊頭。

那么,問題來了,如果把一個(100 * 100)的圖片放到(800 * 800)的Imageview中會怎么樣呢?由上可知,Glide會為每個不同尺寸的Imageview緩存一張圖片,也就是說不管這張圖片有沒有加載過,只要Imageview的尺寸不一樣,Glide就會重新加載一次,這時候,它會在加載的Imageview之前從網絡上重新下載,然后再緩存。舉個例子,如果一個頁面的Imageview是100 * 100,另一個頁Imageview是800 * 800,它倆展示同一張圖片的話,Glide會下載兩次圖片,并且緩存兩張圖片,因為Glide緩存Key的生成條件之一就是控件的長寬。

除了緩存,Glide還有一點我覺得做的非常好,就是在圖片加載中關閉頁面,此頁面也不會造成內存泄漏,因為Glide在加載資源的時候,如果是在 Activity,Fragment 這類有生命周期的組件上進行的話,會創建一個無界面的Fragment加入到FragmentManager之中,感知生命周期,當 Activity,Fragment進入不可見,或者已經銷毀的時候,Glide會停止加載資源。但是如果,是在非生命周期的組件上進行時,會采用Application的生命周期貫穿整個應用,此時只有在應用程序關閉的時候才會停止加載。

到此,相信大家對“Android圖片框架Glide原理是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

观塘区| 濮阳市| 专栏| 博乐市| 英山县| 西华县| 龙门县| 南靖县| 南皮县| 临澧县| 台东县| 徐闻县| 临城县| 巴中市| 榆树市| 枣庄市| 波密县| 平利县| 双鸭山市| 炎陵县| 崇左市| 长顺县| 江达县| 东光县| 牟定县| 仁怀市| 台前县| 沁源县| 东乡| 安西县| 宝鸡市| 灵山县| 建宁县| 文水县| 开平市| 正镶白旗| 江达县| 东平县| 西和县| 灵璧县| 南宁市|