Glide的三个坑

这篇文章,专门吐槽下glide的三点不合理设计(至少个人认为不合理)

吐槽1:Bitmap recycle

相信有不少项目,在线上环境,都有碰到类似的崩溃吧

java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@f08cd43
at android.graphics.Canvas.throwIfCannotDraw(Canvas.java:1270)
at android.graphics.Canvas.drawBitmap(Canvas.java:1404)
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:544)
at android.widget.ImageView.onDraw(ImageView.java:1244)
at android.view.View.draw(View.java:16669)
at android.view.View.updateDisplayListIfDirty(View.java:15622)

崩溃log只有系统层面的堆栈,这个问题在我之前文章已经有分析过了,原因是因为glide主动回收了bitmap导致的(当然也有可能是其他代码异常,不过我之前项目线上的这种崩溃,最终排查,都是glide导致的)

先来看下glide内部回收bitmap的代码

# com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool#trimToSize
private synchronized void trimToSize(long size) {                       
  while (currentSize > size) {                                          
    final Bitmap removed = strategy.removeLast();                    
    if (removed == null) {     
      currentSize = 0;                                                  
      return;                                                           
    }                                                                   
    tracker.remove(removed);                                            
    currentSize -= strategy.getSize(removed);                           
    evictions++;                                                   
    dump(); 
    //就是这里,主动recycle
    removed.recycle();                                                  
  }                                                                     
}                                                                       

当glide的内存缓存池满掉后,就会释放多余的bitmap,而被释放的bitmap,会被主动recycle,但业务层因为一些原因,不小心持有glide加载的bitmap,而这个bitmap又被glide回收了,就会报上面的trying to use a recycled bitmap崩溃了

这种崩溃的,大概率是采用下面的写法导致的

Glide.with(context).asBitmap().load(imageUrl).into(object :CustomTarget<Bitmap>(){        
    override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {  
        //这里拿到了bitmap,去做自己业务的事情                                                           
    }                                                                                     
                                                                                          
    override fun onLoadCleared(placeholder: Drawable?) {                                  
        //这里回调没有实现,去释放上面引用的bitmap
    }                                                                                     
})                                                                                        

修复措施:在onLoadCleared回调里释放在onResourceReady拿到的Bitmap的引用

其实glide内部的文档,也有对onLoadCleared回调做了清晰的说明,不过很容易被人遗漏

图片

You must ensure that any current Drawable received in onResourceReady(Object, Transition) is no longer used,说明文档,还专门黑体强调了must,需要释放对bitmap的引用

对glide的建议优化措施

不要主动recycle bitmap,把bitmap引用置空,剩余的交给GC去回收就好

bitmap其实可以不用主动recycle,就算调用了recycle也不会立即马上被recycle,官方的文档也有说明,recycle一般没必要主动调用,GC系统会自行处理

图片

吐槽2:页面回收后,加载图片崩溃

这个问题,也是线上经常发生的,在一些极端情况下,页面被回收了,调用glide去加载图片,产生了崩溃

IllegalArgumentException: You cannot start a load for a destroyed activity
    at com.b.a.e.m.b(RequestManagerRetriever.java:311)
    at com.b.a.e.m.a(RequestManagerRetriever.java:130)
    at com.b.a.e.m.a(RequestManagerRetriever.java:114)

这个崩溃的产生的代码如下

# com.bumptech.glide.manager.RequestManagerRetriever#assertNotDestroyed
  private static void assertNotDestroyed(@NonNull Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
      throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
    }
  }

加载图片的时候,glide内部判断页面已经销毁,就直接触发崩溃。

其实线上环境复杂,还有不保留等场景,在一些极端情况下,还是很容易出现页面销毁后,还去加载图片的情况

修复措施:加载图片前,主动做下判断

/**
 * 新增判断当前的页面是否已经销毁了
 */
fun Context.isValid(): Boolean {
    val context = this
    if (context is FragmentActivity) {
        if (context.isFinishing || context.isDestroyed) {
            return false
        }
    }
    return true
}

最好是所有的glide加载,走统一封装的方法,这样的话,也好统一做容错

对glide的建议优化措施

页面销毁后,加载图片不响应并且增加warning的log,没必要触发崩溃

吐槽3:bitmap放大

我们都知道glide加载的bitmap会自动适应目标imageview的大小,不过如果是小图加载到一个大的imageview上面呢?

glide会默认把bitmap放大,用以填充Imageview,增加了内存占用,特别是长图,会导致内存极大增加

比如一张800*2400的bitmap,原始大小为7M,加载在一个宽度为1440的手机尺寸上,高度自适应,那实际imageview的尺寸为:1440 *4320,glide会把biamap缩放到跟imageview一样大,加载后的bitmap大小为23M

本来是一张7M的图片,实际在手机内存中,占用了23M的内存,根本原因是glide内部的DownsampleStrategy决定的,相关的代码如下

# com.bumptech.glide.load.resource.bitmap.DownsampleStrategy.CenterOutside#getScaleFactor
public float getScaleFactor(                                                      
    int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) { 
  float widthPercentage = requestedWidth / (float) sourceWidth;                   
  float heightPercentage = requestedHeight / (float) sourceHeight;                
  return Math.max(widthPercentage, heightPercentage);                             
}                                                                                 

这里会计算bitmap的缩放值,sourceWidth、sourceHeight是bitmap的原始宽高,requestedWidth、requestedHeight是imageview的宽高,当imageview比bitmap大的时候,bitmap会被放大

修复措施:加载小图到大的imageview,增加加载配置,避免图片被放大

# 配置方式1,增加override(Target.SIZE_ORIGINAL)
Glide.with(this).override(Target.SIZE_ORIGINAL).load(imageUrl).into(target)

# 配置方式2,采用其他DownsampleStrategy
Glide.with(this).downsample(DownsampleStrategy.CENTER_INSIDE).load(imageUrl).into(target)

这样的话,就可以避免bitmap被放大了;不过这个场景,适合明确知道图片bitmap的尺寸比imageview的尺寸小才适合

对glide的建议优化措施

imageview如果比bitmap大,默认不要放大bitmap,用原始bitmap尺寸展示就好

转自:https://mp.weixin.qq.com/s/zFeaOg2xKX90fIdf_71IDw?st=F7807030B3AD16F4A2DFEF62310FF5BEAD3C67A4E362BA91D9D9817533007595486991D8391B4E6FD455FA04D1144EBA8D8E2BA09AF90C722FDB8A819240F6DDD37CB32249A67D07F62F6580875946935BA43A50056DA7D4979382740EED83DE3E728BED2900911BF4CB6938A84B01118E2F43C6360B6C46A33843C769418CBAC376D68C83F11C0004250F164A7DC05C7D06D0640B741957775CD9C00CCCEDBE3780745D4790A61081FE25221D71E8E2&vid=1688850467124544&cst=929E8BB8EF0A8EBD0D53E909E2918B691BE366E8CFBA74AADD412D90FFB5924AFE4C2CD0B06B50DDFA2C443BAE5731A7&deviceid=39a61f78-0c4e-491f-ac13-49b6c78704e2&version=3.1.8.90238&platform=mac

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值