Glide源碼解析之ActiveResources

前言

在之前我們看Glide獲取數(shù)據(jù)的時候,第一個就是從ActiveResource中獲取的,作為第一級緩存,那么它究竟是個什么東西,下面讓我們來揭開它的神秘面紗。

第一級緩存

這里的代碼很簡單,從ActiveResource中根據(jù)key獲取EngineResource,由此我們可以猜測ActiveResource很有可能是由Map來保存數(shù)據(jù)的。

    //Engine.load()
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
        cb.onResourceReady(active, DataSource.MEMORY_CACHE);
        return null;
    }
    
    private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
            return null;
        }
        EngineResource<?> active = activeResources.get(key);
        if (active != null) {
            active.acquire();
        }

        return active;
    }

ActiveResource源碼

在構(gòu)造函數(shù)中對變量isActiveResourceRetentionAllowed和monitorClearedResourcesExecutor賦值,在Engine中調(diào)用的是它的第一個構(gòu)造函數(shù),那么它實際使用的是一個只有一個線程的線程池,并且設(shè)置優(yōu)先級為后臺線程,然后就開始執(zhí)行cleanReferenceQueue()。

其次用HashMap保存了ResourceWeakReference,也證實了上面的猜想。ResourceWeakReference這個類在下面會講到。

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

isActiveResourceRetentionAllowed代表是否保留活動資源,可以通過GlideBuilder賦值,默認為false。如果設(shè)置為true則Glide會持有底層的資源(比如Bitmap)的強引用用來做內(nèi)存緩存。

    //GlideBuilder
    private boolean isActiveResourceRetentionAllowed;
    
    /**
     *  Defaults to {@code false}.
     */
    public GlideBuilder setIsActiveResourceRetentionAllowed(
            boolean isActiveResourceRetentionAllowed) {
        this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
        return this;
    }

在看cleanReferenceQueue()之前先來看下ActiveResource的靜態(tài)內(nèi)部類ResourceWeakReference,稍后會用到。

ResourceWeakReference

這里繼承了WeakReference來持有EngineResource,這樣當只有弱引用持有EngineResource的時候如果發(fā)生了gc則會回收掉EngineResource。

同時將ActiveResource的ReferenceQueue傳入,這樣當EngineResource被回收時就能知道了。

默認情況下變量resource的值為null,isCacheable的值為true。

    static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
        @Synthetic
        final Key key;
        @Synthetic
        final boolean isCacheable;

        @Nullable
        @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();   //BaseRequestOptions中的值默認為true
        }
        
        //清除資源
        void reset() {
            resource = null;
            clear();
        }

看完ResourceWeakReference之后接下來開始看cleanReferenceQueue(),主要就是在while循環(huán)里面調(diào)用了resourceReferenceQueue的remove(),這個方法會一直阻塞當前線程,直到有返回值。當ResourceWeakReference里面的EngineResource被內(nèi)存回收掉的時候才會有返回值,所以這里用線程池開了一個線程來處理。接著執(zhí)行cleanupActiveReference(),把HashMap中保存的ResourceWeakReference刪除。如果在GlideBuilder中設(shè)置了isActiveResourceRetentionAllowed為true則會接著執(zhí)行下面的方法,把實際的資源重新生成一個EngineResource,并回調(diào)給Engin讓它存入MemoryCache中。

    void cleanReferenceQueue() {
        while (!isShutdown) {
            try {
                ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
                cleanupActiveReference(ref);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
        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);
            }
        }
    }

    //Engine
    public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
        activeResources.deactivate(cacheKey);
        if (resource.isCacheable()) {
            cache.put(cacheKey, resource);      //MemoryCache
        } else {
            resourceRecycler.recycle(resource);
        }
    }
    
    //ActiveResources
    synchronized void deactivate(Key key) {
        ResourceWeakReference removed = activeEngineResources.remove(key);
        if (removed != null) {
            removed.reset();
        }
    }

看完了監(jiān)聽內(nèi)存回收的邏輯,接下來看下是如何從ActiveResource獲取EngineResource的。直接從HashMap中根據(jù)key取ResourceWeakReference,如果沒被回收則再取里面的EngineResource,如果已經(jīng)被回收了則執(zhí)行清除工作。

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

ActiveResource并沒有提供put()來保存數(shù)據(jù),取而代之的是activate()。在Engine的load()中如果一開始在ActiveResource沒有獲取到EngineResource,則接下來會在MemoryCache中獲取,如果獲取到則會把EnginResource保存到ActiveResource。

    //Engine
    private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
            return null;
        }

        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
            cached.acquire();
            activeResources.activate(key, cached);
        }
        return cached;
    }
    
    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();
        }
    }

總結(jié)

ActiveResource做為Glide的第一級緩存,保存的是那些活躍的EngineResource,即沒有被內(nèi)存回收的數(shù)據(jù)。這里對緩存的大小沒有限制,防止因為同時加載的圖片太多造成了MemoryCache因為大小限制而移出緩存,導(dǎo)致最終去使用磁盤緩存的問題。同時使用了弱引用,保證了當進行內(nèi)存回收時能及時回收掉,避免一直占用內(nèi)存。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容