Glide之ActiveResource

ActiveResource是Glide中的一個內存緩存類,這個內存緩存類和LruResourceCache內存緩存類是不一樣的。

EngineResource只有被正在使用,也就是有對象持有它,它纔會被緩存到ActiveResource中,EngineResource中有個acquire變量表示有幾個地方持有了這個Resource,如果acquire等於0了,這時纔會把EngineResorce從ActiveResource中移除放到LruResourceCache中。這個是正常的流程,具體分析可以看Glide之EngineJob和EngineResource。這個是在主線程中做的。

可是還有一種情況,稱之爲特殊情況,就是在gc的時候,EngineResource的擁有者被回收了,我們假設EngineResource的所有擁有者都被釋放了,按道理,EngineResourece也會被釋放的,問題來了,如果ActiveResource中緩存數據是採用強引用方式存儲EngineResource的話就會導致內存泄漏,因爲ActiveResource並不知道EngineResource可以被釋放了。

爲了避免這個問題,ActiveResources纔有了弱引用的方式,這樣就可以避免上面內存泄漏的問題了。我們具體分析一下,因爲採用了WeakReference,EngineResource所有的強引用都斷開了,EngineResource就會被釋放,包裝EngineResource的Reference就會被放到ReferenceQueue裏面。這樣雖然解決了內存泄漏的問題,可是EngineResource從ActiveResources釋放後沒有放到LruCache中,這樣也不對呀。

爲了解決這個問題,ActiveResource啓動了一個線程專門去掃描ReferenceQueue,當然這個線程的優先級很低是THREAD_PRIORITY_BACKGROUND。只要調用ReferenceQueue的remove方法能取到元素,就說明EngineResource被回收了,可以移動到LruCache裏面了。不過在使用過程中有個技巧,Reference包裝了EngineResource,在回收的時候,被包裝的EngineResource是會被回收的,可是我們還要把它保存到LruCache中,所以,在WeakReference的繼承類中有個變量引用了EngineResource。

EngineResourc從ActiveResources移動到LruCache中是在Engine中進行的。

ActiveResources中是用HashMap,value是WeakReference的方式緩存的,因爲它是短期緩存的,所以沒有限制大小。

大家對照上面的解釋,看源碼就很好理解了

package com.bumptech.glide.load.engine;

import android.os.Process;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.engine.EngineResource.ResourceListener;
import com.bumptech.glide.util.Executors;
import com.bumptech.glide.util.Preconditions;
import com.bumptech.glide.util.Synthetic;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;

final class ActiveResources {
  private final boolean isActiveResourceRetentionAllowed;
  private final Executor monitorClearedResourcesExecutor;
  @VisibleForTesting
  final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
  private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();

  private ResourceListener listener;

  private volatile boolean isShutdown;
  @Nullable
  private volatile DequeuedResourceCallback cb;

  ActiveResources(boolean isActiveResourceRetentionAllowed) {
    this(
        isActiveResourceRetentionAllowed,
        java.util.concurrent.Executors.newSingleThreadExecutor(
            new ThreadFactory() {
              @Override
              public Thread newThread(@NonNull final Runnable r) {
                return new Thread(
                    new Runnable() {
                      @Override
                      public void run() {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        r.run();
                      }
                    },
                    "glide-active-resources");
              }
            }));
  }

  @VisibleForTesting
  ActiveResources(
      boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
    this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
    this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;

    monitorClearedResourcesExecutor.execute(
        new Runnable() {
          @Override
          public void run() {
            cleanReferenceQueue();
          }
        });
  }

  void setListener(ResourceListener listener) {
    synchronized (listener) {
      synchronized (this) {
        this.listener = listener;
      }
    }
  }

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

  synchronized void deactivate(Key key) {
    ResourceWeakReference removed = activeEngineResources.remove(key);
    if (removed != null) {
      removed.reset();
    }
  }

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

  @SuppressWarnings({"WeakerAccess", "SynchronizeOnNonFinalField"})
  @Synthetic
  void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
    // Fixes a deadlock where we normally acquire the Engine lock and then the ActiveResources lock
    // but reverse that order in this one particular test. This is definitely a bit of a hack...
    synchronized (listener) {
      synchronized (this) {
        activeEngineResources.remove(ref.key);

        if (!ref.isCacheable || ref.resource == null) {
          return;
        }
        EngineResource<?> newResource =
            new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
        newResource.setResourceListener(ref.key, listener);
        listener.onResourceReleased(ref.key, newResource);
      }
    }
  }

  @SuppressWarnings("WeakerAccess")
  @Synthetic void cleanReferenceQueue() {
    while (!isShutdown) {
      try {
        ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
        cleanupActiveReference(ref);

        // This section for testing only.
        DequeuedResourceCallback current = cb;
        if (current != null) {
          current.onResourceDequeued();
        }
        // End for testing only.
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    }
  }

  @VisibleForTesting
  void setDequeuedResourceCallback(DequeuedResourceCallback cb) {
    this.cb = cb;
  }

  @VisibleForTesting
  interface DequeuedResourceCallback {
    void onResourceDequeued();
  }

  @VisibleForTesting
  void shutdown() {
    isShutdown = true;
    if (monitorClearedResourcesExecutor instanceof ExecutorService) {
      ExecutorService service = (ExecutorService) monitorClearedResourcesExecutor;
      Executors.shutdownAndAwaitTermination(service);
    }
  }

  @VisibleForTesting
  static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
    @SuppressWarnings("WeakerAccess") @Synthetic final Key key;
    @SuppressWarnings("WeakerAccess") @Synthetic final boolean isCacheable;

    @Nullable @SuppressWarnings("WeakerAccess") @Synthetic Resource<?> resource;

    @Synthetic
    @SuppressWarnings("WeakerAccess")
    ResourceWeakReference(
        @NonNull Key key,
        @NonNull EngineResource<?> referent,
        @NonNull ReferenceQueue<? super EngineResource<?>> queue,
        boolean isActiveResourceRetentionAllowed) {
      super(referent, queue);
      this.key = Preconditions.checkNotNull(key);
      this.resource =
          referent.isCacheable() && isActiveResourceRetentionAllowed
              ? Preconditions.checkNotNull(referent.getResource()) : null;
      isCacheable = referent.isCacheable();
    }

    void reset() {
      resource = null;
      clear();
    }
  }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章