关于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有三个实现类


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 to use a recycled bitmap错误的问题分析)