4

简单聊聊Glide的内存缓存

 2 years ago
source link: http://www.androidchina.net/12037.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

简单聊聊Glide的内存缓存 – Android开发中文站

你的位置:Android开发中文站 > 热点资讯 > 简单聊聊Glide的内存缓存

glide的内存缓存有两级:LruCache、ActiveResources

其中LruCache老生常谈了,这里就不细说了。

ActiveResources实际上内含一个HashMap,Map中value则是资源的弱引用。

那么这两级是如何工作的?

先从LruCache取,没有再从ActiveResources取

如果LruCache中有,则取出存入ActiveResources,并从LruCache移除

代码如下:

public class Engine implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    ...    

    public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
            DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
            Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
        Util.assertMainThread();
        long startTime = LogTime.getLogTime();

        final String id = fetcher.getId();
        //生成缓存的key
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());
        //从LruCache获取缓存图片
        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
            cb.onResourceReady(cached);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from cache", startTime, key);
            }
            return null;
        }
        //从弱引用获取图片
        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
            cb.onResourceReady(active);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from active resources", startTime, key);
            }
            return null;
        }

        EngineJob current = jobs.get(key);
        if (current != null) {
            current.addCallback(cb);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Added to existing load", startTime, key);
            }
            return new LoadStatus(cb, current);
        }

        EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
        DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
                transcoder, diskCacheProvider, diskCacheStrategy, priority);
        EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
        jobs.put(key, engineJob);
        engineJob.addCallback(cb);
        engineJob.start(runnable);

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
    }

    ...
}

如果内存本地都没有,则从网络获取,获取后先存入ActiveResources,ActiveResources中存储的是EngineResource对象的弱引用。

EngineResource是将资源进行封装的一个类,它有一个计数acquired,记录资源被引用的次数,当资源被取出使用时+1(acquired函数),当资源被释放时-1(release函数)。当acquired为0时,会将它从ActiveResources中移除,存入LruCache。

代码如下:

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

listener是Engine对象

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

可以看到如果开启内存缓存,则存入LruCache,否则直接释放。

这样我们就比较明白glide内存的两级缓存是怎么回事了,实际上是对缓存的资源进行了划分:使用中的和使用过的。

使用中的放入ActiveResources,这样可以防止被LruCache算法回收掉;而使用过的放在LruCache中,通过算法控制内存总量。

release何时执行

上面我们知道当资源被使用时会调用EngineResource的acquired函数,释放的时候会调用EngineResource的release函数。

使用的时候我们比较好理解,取出的时候其实就是使用的时候,这是一个主动的动作。

但是何时释放?glide中是怎么监控资源释放的?

通过查找EngineResource的release函数的调用,找到在Engine中

public void release(Resource<?> resource) {
  if (resource instanceof EngineResource) {
    ((EngineResource<?>) resource).release();
  } else {
    throw new IllegalArgumentException("Cannot release anything but an EngineResource");
  }
}

继续查找这个函数在哪里调用,发现在SingleRequest中

@Override
public synchronized void clear() {
  assertNotCallingCallbacks();
  stateVerifier.throwIfRecycled();
  if (status == Status.CLEARED) {
    return;
  }
  cancel();
  // Resource must be released before canNotifyStatusChanged is called.
  if (resource != null) {
    releaseResource(resource);
  }
  if (canNotifyCleared()) {
    target.onLoadCleared(getPlaceholderDrawable());
  }

  status = Status.CLEARED;

  if (toRelease != null) {
    engine.release(toRelease);
  }
}

那么这个clear函数又在哪里调用?在ViewTarget中

@Synthetic void pauseMyRequest() {
  Request request = getRequest();
  // If the Request were cleared by the developer, it would be null here. The only way it's
  // present is if the developer hasn't previously cleared this Target.
  if (request != null) {
    isClearedByUs = true;
    request.clear();
    isClearedByUs = false;
  }
}

ViewTarget是对要加载图片的ImageView进行封装,而资源的释放也必然与View有关系。

ViewTarget有一个字段protected final T view;这就是要加载图片的ImageView,另外在ViewTarget中可以看到对这个view添加了attach监听:

view.addOnAttachStateChangeListener(attachStateListener);

这个attachStateListener的源码:

attachStateListener = new OnAttachStateChangeListener() {
  @Override
  public void onViewAttachedToWindow(View v) {
    resumeMyRequest();
  }

  @Override
  public void onViewDetachedFromWindow(View v) {
    pauseMyRequest();
  }
};

这样就很明显了,每个加载图片的view都会注册一个OnAttachStateChangeListener,当这个view从界面移除的时候,也就是资源不再被引用的时候,就会调用pauseMyRequest,最终会将EngineResource的引用计数-1。

这样就保证了当ActiveResources中的资源不再被引用时,将这个资源转移到LruCache中。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK