Glide源码解析之ActiveResources

作者: 断了谁的弦 | 来源:发表于2020-04-25 10:09 被阅读0次

    前言

    在之前我们看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<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。如果设置为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<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循环里面调用了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

        本文链接:https://www.haomeiwen.com/subject/djehwhtx.html