Glide源码解析之ActiveResources

前言

在之前我们看Glide获取数据的时候,第一个就是从ActiveResource中获取的,作为第一级缓存,那么它究竟是个什么东西,下面让我们来揭开它的神秘面纱。

第一级缓存

这里的代码很简单,从ActiveResource中根据key获取EngineResource,由此我们可以猜测ActiveResource很有可能是由Map来保存数据的。

    //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源码

在构造函数中对变量isActiveResourceRetentionAllowed和monitorClearedResourcesExecutor赋值,在Engine中调用的是它的第一个构造函数,那么它实际使用的是一个只有一个线程的线程池,并且设置优先级为后台线程,然后就开始执行cleanReferenceQueue()。

其次用HashMap保存了ResourceWeakReference,也证实了上面的猜想。ResourceWeakReference这个类在下面会讲到。

final class ActiveResources {
    private final boolean isActiveResourceRetentionAllowed;
    private final Executor monitorClearedResourcesExecutor;
    @VisibleForTesting
    final Map activeEngineResources = new HashMap<>();
    private final ReferenceQueue> 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。如果设置为true则Glide会持有底层的资源(比如Bitmap)的强引用用来做内存缓存。

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

在看cleanReferenceQueue()之前先来看下ActiveResource的静态内部类ResourceWeakReference,稍后会用到。

ResourceWeakReference

这里继承了WeakReference来持有EngineResource,这样当只有弱引用持有EngineResource的时候如果发生了gc则会回收掉EngineResource。

同时将ActiveResource的ReferenceQueue传入,这样当EngineResource被回收时就能知道了。

默认情况下变量resource的值为null,isCacheable的值为true。

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

        @Nullable
        @Synthetic
        Resource resource;

        @Synthetic
        @SuppressWarnings("WeakerAccess")
        ResourceWeakReference(
                @NonNull Key key,
                @NonNull EngineResource referent,
                @NonNull ReferenceQueue> 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循环里面调用了resourceReferenceQueue的remove(),这个方法会一直阻塞当前线程,直到有返回值。当ResourceWeakReference里面的EngineResource被内存回收掉的时候才会有返回值,所以这里用线程池开了一个线程来处理。接着执行cleanupActiveReference(),把HashMap中保存的ResourceWeakReference删除。如果在GlideBuilder中设置了isActiveResourceRetentionAllowed为true则会接着执行下面的方法,把实际的资源重新生成一个EngineResource,并回调给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();
        }
    }

看完了监听内存回收的逻辑,接下来看下是如何从ActiveResource获取EngineResource的。直接从HashMap中根据key取ResourceWeakReference,如果没被回收则再取里面的EngineResource,如果已经被回收了则执行清除工作。

    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()来保存数据,取而代之的是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();
        }
    }

总结

ActiveResource做为Glide的第一级缓存,保存的是那些活跃的EngineResource,即没有被内存回收的数据。这里对缓存的大小没有限制,防止因为同时加载的图片太多造成了MemoryCache因为大小限制而移出缓存,导致最终去使用磁盘缓存的问题。同时使用了弱引用,保证了当进行内存回收时能及时回收掉,避免一直占用内存。

你可能感兴趣的:(Glide源码解析之ActiveResources)