欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android圖片框架Glide原理深入探索

 更新時(shí)間:2022年11月03日 16:08:14   作者:明智的健哥  
對(duì)于Glide這個(gè)加載圖片的框架,很多人都在用,我之前使用的是ImageLoader,最近查資料時(shí),發(fā)現(xiàn)Glide才是Google推薦的加載圖片框架,功能非常強(qiáng)大,而且還有Google專人維護(hù),要知道,ImageLoader已經(jīng)沒人維護(hù)了,除了問題可沒人解答。所以有必要整理一下Glide的使用

首先引入依賴

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

下面一行代碼,就是Glide最簡(jiǎn)單的使用方式了

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

with

首先,我們來看with,其實(shí)with的功能就是根據(jù)傳入的context來獲取圖片請(qǐng)求管理器RequestManager,用來啟動(dòng)和管理圖片請(qǐng)求。

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

context可以傳入app,activity和fragment,這關(guān)系著圖片請(qǐng)求的生命周期。通常使用當(dāng)前頁面的context,這樣當(dāng)我們打開一個(gè)頁面加載圖片,然后退出頁面時(shí),圖片請(qǐng)求會(huì)跟隨頁面銷毀而被取消,而不是繼續(xù)加載浪費(fèi)資源。

當(dāng)context是app時(shí),獲得的RequestManager是一個(gè)全局單例,圖片請(qǐng)求的生命周期會(huì)跟隨整個(gè)app。

注意:如果with發(fā)生在子線程,不管context是誰,都返回應(yīng)用級(jí)別的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;
  }

當(dāng)context是Activity時(shí),會(huì)創(chuàng)建一個(gè)無界面的fragment添加到Activity,用于感知Activity的生命周期,同時(shí)創(chuàng)建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方法會(huì)得到一個(gè)圖片請(qǐng)求構(gòu)建器RequestBuilder,用來創(chuàng)建圖片請(qǐng)求。

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

into

首先是根據(jù)ImageView的ScaleType,來配置參數(shù)

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

繼續(xù)跟進(jìn)into,會(huì)創(chuàng)建圖片請(qǐng)求,獲取Target載體已有的請(qǐng)求,對(duì)比兩個(gè)請(qǐng)求,如果等效,啟動(dòng)異步請(qǐng)求。然后,圖片載體綁定圖片請(qǐng)求,也就是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;
  }

繼續(xù)跟進(jìn)異步請(qǐng)求 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();//開啟圖片請(qǐng)求
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);//如果是暫停狀態(tài),就把請(qǐng)求存起來
    }
  }

到這里就啟動(dòng)了圖片請(qǐng)求了,我們繼續(xù)跟進(jìn)request.begin()

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

然后繼續(xù)瞧瞧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);
      //......
    }
  }

跟進(jìn)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) {
      //從內(nèi)存加載
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
      if (memoryResource == null) { //如果內(nèi)存里沒有
        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是一個(gè)Runnable,它通過一系列的調(diào)用,會(huì)來到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());
      //回調(diào)出去
      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));
      }
    }
  }

至此,網(wǎng)絡(luò)請(qǐng)求結(jié)束,最后把圖片設(shè)置上去就行了,在SingleRequest的onResourceReady方法,它會(huì)把結(jié)果回調(diào)給Target載體

target.onResourceReady(result, animation);

繼續(xù)跟進(jìn)它,最終會(huì)執(zhí)行setResource,把圖片設(shè)置上去

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

原理總結(jié)

with根據(jù)傳入的context來獲取圖片請(qǐng)求管理器RequestManager,當(dāng)傳入的context是App時(shí),獲得的RequestManager是一個(gè)全局單例,圖片請(qǐng)求的生命周期會(huì)跟隨這個(gè)應(yīng)用,當(dāng)傳入的是Activity時(shí),會(huì)創(chuàng)建一個(gè)無界面的空fragment添加到Activity,用來感知Activity的生命周期。load會(huì)得到了一個(gè)圖片請(qǐng)求構(gòu)建器RequestBuilder,用來創(chuàng)建圖片請(qǐng)求。into開啟加載,先會(huì)根據(jù)ImageView的ScaleType來配置參數(shù),創(chuàng)建圖片請(qǐng)求,圖片載體綁定圖片請(qǐng)求,然后開啟圖片請(qǐng)求,先從內(nèi)存中加載,如果內(nèi)存里沒有,會(huì)創(chuàng)建一個(gè)Runnable,通過一系列的調(diào)用,使用HttpURLConnection獲取網(wǎng)絡(luò)輸入流,把結(jié)果回調(diào)出去,最后把回調(diào)結(jié)果設(shè)置上去就OK了。

緩存

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

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

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

不過,這也會(huì)產(chǎn)生一個(gè)問題,就是Glide加載圖片時(shí),URL不變但是圖片變了,這種情況會(huì)還是以前的舊圖片。因?yàn)镚lide加載圖片會(huì)將圖片緩存到本地,如果url不變則直接讀取緩存不會(huì)再網(wǎng)絡(luò)加載。

解決方案:

清除緩存讓后臺(tái)每次都更改圖片的名字圖片地址選用 ”url?key="+隨機(jī)數(shù)這種格式

LruCache

LruCache就是維護(hù)一個(gè)緩存對(duì)象列表,其中對(duì)象列表的排列方式是按照訪問順序?qū)崿F(xiàn)的,即一直沒訪問的對(duì)象,將放在隊(duì)尾,即將被淘汰。而最近訪問的對(duì)象將放在隊(duì)頭,最后被淘汰。其內(nèi)部維護(hù)了一個(gè)集合LinkedHashMap,LinkHashMap繼承HashMap,在HashMap的基礎(chǔ)上,新增了雙向鏈表結(jié)構(gòu),每次訪問數(shù)據(jù)的時(shí)候,會(huì)更新被訪問數(shù)據(jù)的鏈表指針,該LinkedHashMap是以訪問順序排序的,當(dāng)調(diào)用put()方法時(shí),就會(huì)在集合中添加元素,判斷緩存是否已滿,如果滿了就用LinkedHashMap的迭代器刪除隊(duì)尾元素,即近期最少訪問的元素。當(dāng)調(diào)用get()方法訪問緩存對(duì)象時(shí),就會(huì)調(diào)用LinkedHashMap的get()方法獲得對(duì)應(yīng)集合元素,同時(shí)會(huì)更新該元素到隊(duì)頭。

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

除了緩存,Glide還有一點(diǎn)我覺得做的非常好,就是在圖片加載中關(guān)閉頁面,此頁面也不會(huì)造成內(nèi)存泄漏,因?yàn)镚lide在加載資源的時(shí)候,如果是在 Activity,F(xiàn)ragment 這類有生命周期的組件上進(jìn)行的話,會(huì)創(chuàng)建一個(gè)無界面的Fragment加入到FragmentManager之中,感知生命周期,當(dāng) Activity,F(xiàn)ragment進(jìn)入不可見,或者已經(jīng)銷毀的時(shí)候,Glide會(huì)停止加載資源。但是如果,是在非生命周期的組件上進(jìn)行時(shí),會(huì)采用Application的生命周期貫穿整個(gè)應(yīng)用,此時(shí)只有在應(yīng)用程序關(guān)閉的時(shí)候才會(huì)停止加載。

到此這篇關(guān)于Android Glide原理深入探索的文章就介紹到這了,更多相關(guān)Android Glide內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論