解决Glide复用ImageView.drawable导致Canvas: trying to use a recycled bitmap

问题

在做一个功能时使用到了Glide来加载图片,Glide.with(requireContext()).placeholder.load 切换图片时如果load返回图片比较慢,这时会闪现一下默认预加载图片,看起来感觉非常不好,为了更丝滑的切换图片这时我想到在placeholder(img)中传入上一次imageview.drawable图像资源,本来是开心的(看起来好丝滑呀!),但是在多次切换之后,啪程序闪现退了,woc!!

java.lang.RuntimeException: Canvas: trying to use a recycled bitmap

哎,意外也来的太快了吧!

不过从报错不难看出来,它想要回收的bitmap对象,我还在使用中,导致报错

解决办法

我这里决定采用复制的方式,旧的资源就让回收吧!

/** 不要用这个,这个方法会让图片尺寸越来越大,导致OOM,请使用下面那个方法,我修复过了
fun copyDrawable(res: Resources, src: Drawable?): Drawable? {
      if (src == null) {
          return null
     }
     val bitmap =
         Bitmap.createBitmap(
             src.intrinsicWidth,
             src.intrinsicHeight,
             Bitmap.Config.ARGB_8888
        )
    val canvas = Canvas(bitmap)
    src.draw(canvas)
    return BitmapDrawable(res, bitmap)
}*/

// 2022/12/02 修复之前方法会出现OOM的BUG
fun copyDrawable(src: Drawable?): Drawable? {
    
    
      if (src == null) {
    
    
          return null
     }
     val bitmap =
         Bitmap.createBitmap(
             src.intrinsicWidth,
             src.intrinsicHeight,
             Bitmap.Config.ARGB_8888
        )
    val canvas = Canvas(bitmap)
    src.draw(canvas)
    return BitmapDrawable(null,bitmap)
}

旧的资源不让用,我造一个新的出来不就行了吗



2022/12/02 今天在不同设备测试中发现了另一个bug 非常严重

BitmapDrawable(res, bitmap)导致原图Drawable尺寸越来越大

今天在使用的时候发现程序频繁更新imageview会OOM,我看到之后就想难道我copy的Gide没有帮我释放?我就手动释放一下结果还是会OOM,我debug看了一下图片尺寸8000x8000多?woc这么大,不可能吧,那好嘛我裁剪一下,裁完之后发现离谱的是,图片第一次是300x300,是我裁剪的大小没错,第二次变成562x562,第三次…反正就越来越大越来越大了,这样看起来不对呀,那应该是其它地方的问题,最后我排查到了copyDrawable,我输出复制之前的尺寸是正常,复制之后就变大了,那不是copyDrawable还能是谁,看到copyDrawable方法bitmap转换出来尺寸都正常,到了BitmapDrawable之后就变大了,我就点进去看了BitmapDrawable源码,下面是部分源代码:

  private void init(BitmapState state, Resources res) {
    
    
        mBitmapState = state;
        updateLocalState(res);

        if (mBitmapState != null && res != null) {
    
    
            mBitmapState.mTargetDensity = mTargetDensity;
        }
    }

    /**
     * Initializes local dynamic properties from state. This should be called
     * after significant state changes, e.g. from the One True Constructor and
     * after inflating or applying a theme.
     */
    private void updateLocalState(Resources res) {
    
    
        mTargetDensity = resolveDensity(res, mBitmapState.mTargetDensity);
        mBlendModeFilter = updateBlendModeFilter(mBlendModeFilter, mBitmapState.mTint,
                mBitmapState.mBlendMode);
        computeBitmapSize();
    }
private void computeBitmapSize() {
    
    
        final Bitmap bitmap = mBitmapState.mBitmap;
        if (bitmap != null) {
    
    
            mBitmapWidth = bitmap.getScaledWidth(mTargetDensity);
            mBitmapHeight = bitmap.getScaledHeight(mTargetDensity);
        } else {
    
    
            mBitmapWidth = mBitmapHeight = -1;
        }
    }

这是Android的BitmapDrawable部分源代码,computeBitmapSize看这个方法它自己又给我重新计算了宽度高度,看到这里就什么都明白了,(内心OS:我复制图片根本不需它缩放呀),当时我只看到BitmapDrawable(bitmap) 被划横线弃用了,看了注释找到了用BitmapDrawable(res, bitmap)代替,我就没想那么多,原来它要res是为了得到屏幕密度,自动缩图片大小,我勒个去,坑啊,既然BitmapDrawable(bitmap) 弃用了,那就这样写BitmapDrawable(null, bitmap)行了吧,最后改完debug看了一下果然好了



nice,搞定!

复制这种方式可能很多的人都想的到,但是我在网上搜索时看见别人提到的Drawable复制不是很管用,于是我把我的代码分享出来。

猜你喜欢

转载自blog.csdn.net/qq_39457683/article/details/127403585