Glide4.9圖片框架源碼(三)之into方法后續(xù)讀取內(nèi)存緩存

上一節(jié)我們解析了為什么Glide能監(jiān)聽頁面的生命周期而保證及時(shí)回收請求和其他資源砰诵,有需要的話看下上一節(jié):

Glide源碼之如何綁定Activity的生命周期

上節(jié)我們講到了Glide.with(this).load(url).into(imageView)前面兩個(gè)方法龙助,這一節(jié)我們看下最重要的一環(huán)into方法,下面先看下into方法的代碼:

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方法的返回值是一個(gè)ViewTarget,ViewTarget是對imageview的一個(gè)封裝样悟,我們后面會講到尚卫,這里第一二行則是判斷是否在主線程和做的一個(gè)非空判斷遏匆,我們看后面的代碼,這里的if語句主要是對ScaleType進(jìn)行設(shè)置码泞,獲取到requestOptions并且設(shè)置ScaleType兄旬,我們看下下面的return語句的into方法,第三個(gè)字段看得出來傳入了一個(gè)線程池余寥,是不是覺得圖片請求快開始了呢领铐,我們先看下glideContext.buildImageViewTarget方法:

public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, 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)");
    }
  }

這里可以看出,我們將imageView傳入imageViewTargetFactory的build方法中宋舷,而這是一個(gè)工廠方法绪撵,針對不同的加載類型生成不同的target,target這里已經(jīng)與imageView同在祝蝠,我們返回看下剛才的into方法:

 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)) {
      request.recycle();
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }

我們直接看buildRequest方法音诈,看命名方式可以猜出,這里應(yīng)該是創(chuàng)建一個(gè)請求了绎狭,這里的代碼比較長细溅,我做了一個(gè)簡化:

  private Request buildRequest(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
    return buildRequestRecursive(
        target,
        targetListener,
        /*parentCoordinator=*/ null,
        transitionOptions,
        requestOptions.getPriority(),
        requestOptions.getOverrideWidth(),
        requestOptions.getOverrideHeight(),
        requestOptions,
        callbackExecutor);
  }
  private Request buildRequestRecursive(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,
      Executor callbackExecutor) {
      .......
    Request mainRequest =
        buildThumbnailRequestRecursive(
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions,
            callbackExecutor);

    if (errorRequestCoordinator == null) {
      return mainRequest;
    }
    .......
    .......
  }
    .......
    .......
  private Request obtainRequest(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      RequestCoordinator requestCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      Executor callbackExecutor) {
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory(),
        callbackExecutor);
  }

  private synchronized void init(
      Context context,
      GlideContext glideContext,
      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) {
    this.context = context;
    this.glideContext = glideContext;
    this.model = model;
    this.transcodeClass = transcodeClass;
    this.requestOptions = requestOptions;
    this.overrideWidth = overrideWidth;
    this.overrideHeight = overrideHeight;
    this.priority = priority;
    this.target = target;
    this.targetListener = targetListener;
    this.requestListeners = requestListeners;
    this.requestCoordinator = requestCoordinator;
    this.engine = engine;
    this.animationFactory = animationFactory;
    this.callbackExecutor = callbackExecutor;
    status = Status.PENDING;

    if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
      requestOrigin = new RuntimeException("Glide request origin trace");
    }
  }

前面的省略號表示不重要的信息,最終調(diào)用到的是init的方法儡嘶,這里為請求request初始化了很多參數(shù)谒兄,例如我們前面設(shè)置過的model,就是請求的URL社付,還有是否重新設(shè)定了加載的寬高,優(yōu)先級邻耕,動畫等等設(shè)置鸥咖,這里就是對請求request的初始化,我們回到最初的into方法:

 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)) {
      request.recycle();
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }

buildRequest下面一行兄世,通過target獲取了一個(gè)previous 的request啼辣,隨后調(diào)用了requestManager.clear(target),這里是保證每次請求都是最新的參數(shù)和設(shè)置御滩,清除上一個(gè)請求鸥拧。這里的 target.setRequest(request);我們點(diǎn)進(jìn)去發(fā)現(xiàn)調(diào)用的是setTag方法,將view和request綁定削解。最后調(diào)用了requestManager.track(target, request)富弦,還記得前面我們?yōu)槭裁凑fRequestManager管理target和request么,我們進(jìn)track方法看下代碼:

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

這里還使用的是同步方法氛驮,這里持有了兩個(gè)追蹤器腕柜,一個(gè)是target,一個(gè)是request,進(jìn)track方法看里面的內(nèi)容:

  private final Set<Target<?>> targets =
      Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());

  public void track(@NonNull Target<?> target) {
    targets.add(target);
  }

  @Override
  public void onStart() {
    for (Target<?> target : Util.getSnapshot(targets)) {
      target.onStart();
    }
  }

  @Override
  public void onStop() {
    for (Target<?> target : Util.getSnapshot(targets)) {
      target.onStop();
    }
  }

  @Override
  public void onDestroy() {
    for (Target<?> target : Util.getSnapshot(targets)) {
      target.onDestroy();
    }
  }

我們發(fā)現(xiàn)targetTracker維護(hù)了一個(gè)set集合盏缤,用于存儲和管理所有的target對象砰蠢,下面的幾個(gè)方法,也正是同步于fragment的和requestmanager的生命周期方法唉铜,便于在出發(fā)生命周期時(shí)對所有的target對象執(zhí)行操作台舱。我們再來看下requestTracker的runRequest方法,看方法命名是不是猜到快要執(zhí)行網(wǎng)絡(luò)請求了呢潭流,來看下代碼:

private final Set<Request> requests =
      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
  private final List<Request> pendingRequests = new ArrayList<>();
  private boolean isPaused;

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

requestTracker同樣維護(hù)了一個(gè)requests Set集合管理所有請求竞惋。isPaused參數(shù)表示如果暫停,那么當(dāng)前的request添加到一個(gè)等待集合中延遲執(zhí)行幻枉,否則直接執(zhí)行begin方法碰声,我們繼續(xù)看下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;
        }
        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");
      }
      if (status == Status.COMPLETE) {
        onResourceReady(resource, DataSource.MEMORY_CACHE);
        return;
      }
      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));
      }
    }
  }

這里調(diào)用的是SingleRequest的begin方法,前面是對當(dāng)前request運(yùn)行狀態(tài)的判斷熬甫,status = Status.WAITING_FOR_SIZE這里直接賦值胰挑,表示當(dāng)前需要等待獲取到view的寬高才能進(jìn)行下一步,如果你在前面的設(shè)置中設(shè)置過override方法傳遞了新的寬高椿肩,那么這里直接使用你傳入的寬高瞻颂,否則調(diào)用target.getSize(this)方法去獲取圖片所加載的view的寬高,我們跟進(jìn)去看這個(gè)getsize方法:

 void getSize(@NonNull SizeReadyCallback cb) {
      int currentWidth = getTargetWidth();
      int currentHeight = getTargetHeight();
      if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
        cb.onSizeReady(currentWidth, currentHeight);
        return;
      }
      if (!cbs.contains(cb)) {
        cbs.add(cb);
      }
      if (layoutListener == null) {
        ViewTreeObserver observer = view.getViewTreeObserver();
        layoutListener = new SizeDeterminerLayoutListener(this);
        observer.addOnPreDrawListener(layoutListener);
      }
    }

我們發(fā)現(xiàn)走到了ViewTarget的getSize方法郑象,ViewTarget正是當(dāng)初我們封裝imageview的target對象贡这,這里直接去獲取getTargetWidth、Height厂榛,我們不進(jìn)去看了盖矫,就是拿到我們之前傳遞的image view,獲取他的寬高击奶。這里的isViewStateAndSizeValid方法則是判斷獲取的寬高是否大于0 并且真實(shí)可用辈双,可用就表示當(dāng)前的image view其實(shí)已經(jīng)繪制完成,我們獲取的到它的寬高并且直接返回柜砾。如果不可用湃望,那么就給image view添加一個(gè)繪制的監(jiān)聽addOnPreDrawListener,當(dāng)繪制完成時(shí)我們再去獲取它的寬高并且返回痰驱。

我們再回到前面的target.getSize方法证芭,繼續(xù)看下去,寬高獲取完成后担映,調(diào)用的是onSizeReady废士,那么我們跟進(jìn)去看下:

public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    synchronized (requestLock) {
      if (IS_VERBOSE_LOGGABLE) {
        logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
      }
      if (status != Status.WAITING_FOR_SIZE) {
        return;
      }
      status = 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);

      if (status != Status.RUNNING) {
        loadStatus = null;
      }
      if (IS_VERBOSE_LOGGABLE) {
        logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

這里的status 直接賦值為RUNNING狀態(tài)了,下面一行的sizeMultiplier變量蝇完,還記得我們第一節(jié)的Glide的常規(guī)使用嗎湃密,它作用域縮略圖诅挑,表示我們想將原圖縮放幾倍大小,這里是獲取設(shè)置并且計(jì)算新的縮略圖寬高泛源,我們不去細(xì)究拔妥,看下面的重點(diǎn),engine.load方法达箍∶涣可以看出,這個(gè)方法傳遞了很多參數(shù)和設(shè)置缎玫,包括參數(shù)設(shè)置廣泛的requestOptions硬纤,這里應(yīng)該即將開始加載請求的信息了,我們繼續(xù)看下去:

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

重點(diǎn)來了赃磨,看第一行的 keyFactory.buildKey方法筝家,返回值是一個(gè)EngineKey,這個(gè)key就是我們用于緩存的key邻辉,再看看入?yún)⒌膸讉€(gè)字段溪王。model,signature值骇,width莹菱,height,options等吱瘩。由此可見道伟,影響一張緩存的圖片,不只是他的url使碾,還包括了要加載的圖片寬高蜜徽、簽名和設(shè)置等,所以當(dāng)同一個(gè)圖片的URL票摇,如果我們加載不同的寬高拘鞋,那么這個(gè)URL是會緩存兩次的。

我們繼續(xù)看有一個(gè)memoryResource對象兄朋,并且調(diào)用了loadFromMemory方法,我們跟進(jìn)去看一下:

  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);
    if (cached != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return cached;
    }
    return null;
  }

第一行我們看到了一個(gè)isMemoryCacheable,這個(gè)就是我們設(shè)置的skipMemoryCache响鹃,如果沒有設(shè)置案训,那么默認(rèn)就是可用的强霎,這里如果設(shè)置了跳過內(nèi)存緩存,那么直接執(zhí)行return語句轩触。繼續(xù)看這里有兩個(gè)loadFrom方法拉馋,一個(gè)是loadFromActiveResources,另一個(gè)是loadFromCache景馁,那么兩個(gè)有什么區(qū)別呢板壮?其實(shí)兩個(gè)都是作為內(nèi)存緩存存在的透葛,一個(gè)內(nèi)部維護(hù)的是LURCACHE萨蚕,另一個(gè)則是hashmap 奕翔,key就是我們傳入的EngineKey庆猫,Value則是我們Resource的一個(gè)弱引用纫普,我們先看下loadFromActiveResources方法:

 @Nullable
  private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }
    return active;
  }

final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
  @Nullable
  synchronized EngineResource<?> get(Key key) {
    ResourceWeakReference activeRef = activeEngineResources.get(key);
    if (activeRef == null) {
      return null;
    }
    EngineResource<?> active = activeRef.get();
    if (active == null) {
      cleanupActiveReference(activeRef);
    }
    return active;
  }

void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
    synchronized (this) {
      activeEngineResources.remove(ref.key);
      if (!ref.isCacheable || ref.resource == null) {
        return;
      }
    }
    EngineResource<?> newResource =
        new EngineResource<>(
            ref.resource, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ false, ref.key, listener);
    listener.onResourceReleased(ref.key, newResource);
  }

 public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);
    }
  }

  synchronized void acquire() {
    if (isRecycled) {
      throw new IllegalStateException("Cannot acquire a recycled resource");
    }
    ++acquired;
  }

loadFromActiveResources方法調(diào)用了getkey方法,實(shí)際上是從activeEngineResources 的hashmap中去取并返回一個(gè)Resource弱引用烤芦,通過這個(gè)弱引用去拿到真正緩存的Resource举娩,如果緩存為空,則調(diào)用cleanupActiveReference方法构罗,第一行activeEngineResources.remove(ref.key)表示铜涉,如果弱引用為空表示被回收了,那么這個(gè)key也將直接移除遂唧。下面判斷如果ref.resource沒有被回收芙代,那么再根據(jù)這個(gè)resource 創(chuàng)建一個(gè)新的Resource對象并調(diào)用onResourceReleased方法。繼續(xù)看這個(gè)方法盖彭,可以看出把它放進(jìn)了cache緩存纹烹,這里的cache緩存就是我們上面說的LRUCACHE內(nèi)存緩存。

繼續(xù)回到loadFromActiveResources召边,然后調(diào)用acquire方法铺呵,看這個(gè)方法做了一個(gè)++的操作,實(shí)際上是對當(dāng)前緩存做了引用計(jì)數(shù)+1掌实,當(dāng)我們每次拿到了緩存陪蜻,引用計(jì)數(shù)就加一邦马,如果銷毀了贱鼻,引用計(jì)數(shù)就減一宴卖,當(dāng)為0的時(shí)候,我們的HashMap則會去回收這個(gè)緩存邻悬。到這里loadFromActiveResources就結(jié)束了症昏,下面我們看下loadFromCache方法:

private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.activate(key, cached);
    }
    return cached;
  }

  private EngineResource<?> getEngineResourceFromCache(Key key) {
    Resource<?> cached = cache.remove(key);
    final EngineResource<?> result;
    if (cached == null) {
      result = null;
    } else if (cached instanceof EngineResource) {
      // Save an object allocation if we've cached an EngineResource (the typical case).
      result = (EngineResource<?>) cached;
    } else {
      result =
          new EngineResource<>(
              cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
    }
    return result;
  }

  synchronized void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);

    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }

第一行調(diào)用了getEngineResourceFromCache,然后直接調(diào)用了cache.remove方法父丰,cache是一個(gè)MemoryCache接口肝谭,實(shí)現(xiàn)類是LruResourceCache,這里直接將獲取到的緩存移除了蛾扇,回到loadFromCache方法攘烛,如果緩存不為空,同樣調(diào)用的引用計(jì)數(shù)方法镀首,然后調(diào)用了activeResources.activate(key, cached)方法坟漱,這里直接創(chuàng)建了一個(gè)弱引用,然后添加到了弱引用的hashmap中更哄。

內(nèi)存緩存的引用計(jì)數(shù)是如何運(yùn)作的芋齿?

前面幾個(gè)章節(jié)只是簡述了存儲和獲取內(nèi)存緩存,沒有仔細(xì)分析引用計(jì)數(shù)和什么時(shí)候觸發(fā)成翩,首先我們回到獲取內(nèi)存緩存的位置看看觅捆,比較下弱引用緩存和內(nèi)存緩存的區(qū)別在哪:

Engine 類
@Nullable
  private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }
    return active;
  }

這里activeResources.get(key)直接從弱引用緩存中獲取,那么我們看這個(gè)activeResources是存在與當(dāng)前的Engine 中的麻敌,要知道我們看active.acquire()方法源碼:

EngineResource 類
 synchronized void acquire() {
    if (isRecycled) {
      throw new IllegalStateException("Cannot acquire a recycled resource");
    }
    ++acquired;
  }
  void release() {
    synchronized (listener) {
      synchronized (this) {
        if (acquired <= 0) {
          throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (--acquired == 0) {
          listener.onResourceReleased(key, this);
        }
      }
    }
  }

@Override
  public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }

我們看到這里的acquired做了一次++栅炒,表示內(nèi)存中引用了當(dāng)前的緩存圖片 ,那么相反的庸论,什么時(shí)候調(diào)用--呢职辅,我們發(fā)現(xiàn)只有release方法做了--操作,當(dāng)acquired=0的時(shí)候調(diào)用了onResourceReleased方法聂示,我們跟進(jìn)去看這個(gè)是干嘛的域携。發(fā)現(xiàn)當(dāng)acquired=0的時(shí)候,activeResources做了清空key和resource的操作鱼喉,然后調(diào)用cache.put(cacheKey, resource)方法轉(zhuǎn)移存儲到了lrucache內(nèi)存緩存秀鞭。那么我們看看這個(gè)release都在什么時(shí)候才回去回收,調(diào)用回收的地方很多扛禽,其中有這樣的一塊:

SingleRequest 類
 public synchronized void clear() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    if (status == Status.CLEARED) {
      return;
    }
    cancel();
    if (resource != null) {
      releaseResource(resource);
    }
    if (canNotifyCleared()) {
      target.onLoadCleared(getPlaceholderDrawable());
    }
    status = Status.CLEARED;
  }

RequestTracker 類
  private boolean clearRemoveAndMaybeRecycle(@Nullable Request request, boolean isSafeToRecycle) {
     if (request == null) {
      return true;
    }
    boolean isOwnedByUs = requests.remove(request);
    isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;
    if (isOwnedByUs) {
      request.clear();
      if (isSafeToRecycle) {
        request.recycle();
      }
    }
    return isOwnedByUs;
  }

RequestManager 類
  @Override
  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }

看到RequestManager的onDestroy方法我想就明白了锋边,引用計(jì)數(shù)和弱引用緩存activeResource是跟activity綁定在一起的,當(dāng)activity銷毀的時(shí)候编曼,那么同時(shí)也會回收activeResource的資源并移動到cache內(nèi)存緩存豆巨,此時(shí)引用計(jì)數(shù)為0,當(dāng)從cache內(nèi)存緩存中取數(shù)據(jù)的時(shí)候掐场,取完則存儲到activeResource緩存往扔,引用計(jì)數(shù)又++ 贩猎。所以這里的引用技術(shù)表示當(dāng)前使用的是activeResource緩存,當(dāng)activeResource緩存即將被回收的時(shí)候萍膛,轉(zhuǎn)移到Cache緩存吭服,引用計(jì)數(shù)--。

總結(jié)

看到這里有頭緒了嗎蝗罗,為什么要設(shè)置兩道緩存艇棕,為什么獲取到的緩存要互相移動,其實(shí)以常規(guī)的思維串塑,這里只需用LRUCACHE即可沼琉,但是這樣的設(shè)計(jì),避免了頻繁去讀取LURCACHE并且如果存儲的圖片資源過多桩匪,lrucache也會回收之前的緩存刺桃,所以為了避免被回收掉,當(dāng)我們從lrucache拿到緩存后立馬移動到hashmap的弱引用緩存吸祟。當(dāng)然弱引用緩存也有可能被系統(tǒng)回收瑟慈,所以當(dāng)弱引用資源還未被回收的時(shí)候,再移動到LRUCACHE緩存中屋匕,兩個(gè)緩存互相利用和結(jié)合葛碧,最大效率的使用了圖片資源。到這里我們的內(nèi)存緩存讀取就分析結(jié)束了过吻,下一節(jié)我們重點(diǎn)分析請求的處理和磁盤緩存进泼。

Glide4.9圖片框架源碼之獲取磁盤緩存和圖片請求

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市纤虽,隨后出現(xiàn)的幾起案子乳绕,更是在濱河造成了極大的恐慌,老刑警劉巖逼纸,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洋措,死亡現(xiàn)場離奇詭異,居然都是意外死亡杰刽,警方通過查閱死者的電腦和手機(jī)菠发,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贺嫂,“玉大人滓鸠,你說我怎么就攤上這事〉谠” “怎么了糜俗?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我悠抹,道長寞射,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任锌钮,我火速辦了婚禮,結(jié)果婚禮上引矩,老公的妹妹穿的比我還像新娘梁丘。我一直安慰自己,他們只是感情好旺韭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布氛谜。 她就那樣靜靜地躺著,像睡著了一般区端。 火紅的嫁衣襯著肌膚如雪值漫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天织盼,我揣著相機(jī)與錄音杨何,去河邊找鬼。 笑死沥邻,一個(gè)胖子當(dāng)著我的面吹牛危虱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播唐全,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼埃跷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了邮利?” 一聲冷哼從身側(cè)響起弥雹,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎延届,沒想到半個(gè)月后剪勿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡方庭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年窗宦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片二鳄。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赴涵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出订讼,到底是詐尸還是另有隱情髓窜,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站寄纵,受9級特大地震影響鳖敷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜程拭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一定踱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恃鞋,春花似錦崖媚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至水由,卻和暖如春荠呐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背砂客。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工泥张, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鞠值。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓圾结,卻偏偏與公主長得像,于是被迫代替她去往敵國和親齿诉。 傳聞我的和親對象是個(gè)殘疾皇子筝野,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345