关于Glide出现trying|关于Glide出现trying to use a recycled bitmap错误的问题分析

老衲上个月在清理项目历史遗留下的疑难问题的时候,被分配了这么了个诡异的bug,当快速进入退出我们项目的时候,如果执行的次数量级够大,则会出现

Glide : trying to use a recycled bitmap balabala......

奇怪的是,其他同事在图片的使用时,已经按照Glide的要求在各个地方去释放内存了。如下
Glide.get(this).clearMemory();

但是,该问题依然如跗骨之蛆一般存在。没法办,只好去撸源码找原因。
clearMemory()方法的内部实现逻辑是
// Engine asserts this anyway when removing resources, fail faster and consistently Util.assertMainThread(); // memory cache needs to be cleared before bitmap pool to clear re-pooled Bitmaps too. See #687. memoryCache.clearMemory(); bitmapPool.clearMemory(); arrayPool.clearMemory();

刚开始的思路是,LruCache内的清理逻辑出了问题。先看下它内部的实现逻辑
Map.Entry last; Iterator> cacheIterator; while (currentSize > size) { cacheIterator= cache.entrySet().iterator(); last = cacheIterator.next(); final Y toRemove = last.getValue(); currentSize -= getSize(toRemove); final T key = last.getKey(); cacheIterator.remove(); onItemEvicted(key, toRemove); }

Lru本质就是个LinkedHashMap,本质就是hashmap的对象清理逻辑,瞅了一顿也没发现有啥不对。于是把目标转移到了Glide的精髓BitmapPool
Glide的一个最重要的思想就是对象池的使用,占内存一样的两个bitmap会进行内存的复用,因为首页图片的尺寸和格式都是同样的,这里肯定会进行bitmap的复用。所以接下来重点BitmapPool的实现逻辑。
BitmapPool有三个实现类

关于Glide出现trying|关于Glide出现trying to use a recycled bitmap错误的问题分析
文章图片
image.png 不解释,直接看LruBitmapPool的clearMemory方法
@Override public void clearMemory() { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "clearMemory"); } trimToSize(0);

}
直接找trimToSize(0)
private synchronized void trimToSize(long size) { while (currentSize > size) { final Bitmap removed = strategy.removeLast(); // TODO: This shouldn't ever happen, see #331. if (removed == null) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Size mismatch, resetting"); dumpUnchecked(); } currentSize = 0; return; } // 划重点!!!!! tracker.remove(removed); currentSize -= strategy.getSize(removed); evictions++; if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Evicting bitmap=" + strategy.logBitmap(removed)); } dump(); removed.recycle(); }

}
重点就在于 tracker.remove(removed)这句代码, tracker???这特么是个毛?继续跟
private interface BitmapTracker { void add(Bitmap bitmap); void remove(Bitmap bitmap); }

这也简单,其实就是一个专门用于添加和删除bitmap的一个工具类,重点来了。。看下LruBitmapPool构造方法里BitmapTracker的实现类:
LruBitmapPool(long maxSize, LruPoolStrategy strategy, Set allowedConfigs) { this.initialMaxSize = maxSize; this.maxSize = maxSize; this.strategy = strategy; this.allowedConfigs = allowedConfigs; this.tracker = new NullBitmapTracker();

}
继续跟NullBitmapTracker:
private static final class NullBitmapTracker implements BitmapTracker {@Synthetic NullBitmapTracker() { }@Override public void add(Bitmap bitmap) { // Do nothing. }@Override public void remove(Bitmap bitmap) { // Do nothing. } }

Do nothing...
noting...
...
Emmm.....我特么....
所以可能的原因是bitmap依然在BitmapPool中,但是已经被recycled了,导致下次使用了复用的bitmap。
解决思路也简单,禁用掉BitmapPool对象池,或者给LruBitmapPool自己设置一个自定义Tracker的实现类。
【关于Glide出现trying|关于Glide出现trying to use a recycled bitmap错误的问题分析】对于常用的开源框架,还是建议同行们多了解下内部的实现逻辑,关键时候派得上用场。

    推荐阅读