简单聊聊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();//生成緩存的keyEngineKey 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的源碼:
總結
以上是生活随笔為你收集整理的简单聊聊Glide的内存缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅析WebRtc中视频数据的接收和渲染流
- 下一篇: mongodb如何快速进行版本升级