前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >glide的三个坑

glide的三个坑

作者头像
韦东锏
发布2021-09-29 15:19:43
2K0
发布2021-09-29 15:19:43
举报
文章被收录于专栏:Android码农

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

吐槽1:Bitmap recycle

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

代码语言:javascript
复制
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的代码

代码语言:javascript
复制
# 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崩溃了

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

代码语言:javascript
复制
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去加载图片,产生了崩溃

代码语言:javascript
复制
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)

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

代码语言:javascript
复制
# 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内部判断页面已经销毁,就直接触发崩溃。

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

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

代码语言:javascript
复制
/**
 * 新增判断当前的页面是否已经销毁了
 */
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决定的,相关的代码如下

代码语言:javascript
复制
# 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,增加加载配置,避免图片被放大

代码语言:javascript
复制
# 配置方式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尺寸展示就好


以上是个人对glide三点吐槽,希望后续版本可以优化

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android码农 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 吐槽1:Bitmap recycle
    • 对glide的建议优化措施
    • 吐槽2:页面回收后,加载图片崩溃
      • 对glide的建议优化措施
      • 吐槽3:bitmap放大
        • 对glide的建议优化措施
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档