Glide trying to use a recycled bitmap android.graphics.Bitmap

现象

Canvas: trying to use a recycled bitmap android.graphics.Bitmap@67d0cbd
ndroid.graphics.BaseCanvas.throwIfCannotDraw(BaseCanvas.java:55)
1 android.graphics.BaseCanvas.throwIfCannotDraw(BaseCanvas.java:55)
2 android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:226)
3 android.view.RecordingCanvas.drawBitmap(RecordingCanvas.java:97)
4 android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:529)
5 android.widget.ImageView.onDraw(ImageView.java:1352)
6 android.view.View.draw(View.java:19439)
7 android.view.View.updateDisplayListIfDirty(View.java:18335)
8 android.view.View.draw(View.java:19143)
9 android.view.ViewGroup.drawChild(ViewGroup.java:4457)
10 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4236)
11 android.view.View.draw(View.java:19447)
12 android.view.View.updateDisplayListIfDirty(View.java:18335)
13 android.view.View.draw(View.java:19143)
14 android.view.ViewGroup.drawChild(ViewGroup.java:4457)
15 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4236)
16 android.view.View.updateDisplayListIfDirty(View.java:18321)
17 android.view.View.draw(View.java:19143)
18 android.view.ViewGroup.drawChild(ViewGroup.java:4457)
19 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4236)
20 android.view.View.updateDisplayListIfDirty(View.java:18321)
21 android.view.View.draw(View.java:19143)
22 android.view.ViewGroup.drawChild(ViewGroup.java:4457)
23 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4236)
24 android.view.View.updateDisplayListIfDirty(View.java:18321)
25 android.view.View.draw(View.java:19143)
26 android.view.ViewGroup.drawChild(ViewGroup.java:4457)
27 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4236)
28 android.view.View.updateDisplayListIfDirty(View.java:18321)
29 android.view.View.draw(View.java:19143)
30 android.view.ViewGroup.drawChild(ViewGroup.java:4457)
31 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4236)
32 android.view.View.updateDisplayListIfDirty(View.java:18321)
33 android.view.View.draw(View.java:19143)
34 android.view.ViewGroup.drawChild(ViewGroup.java:4457)
35 android.view.ViewGroup.dispatchDraw(ViewGroup.java:4236)
36 android.view.View.draw(View.java:19447)
37 com.android.internal.policy.DecorView.draw(DecorView.java:985)
38 android.view.View.updateDisplayListIfDirty(View.java:18335)
39 android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:669)
40 android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:675)
41 android.view.ThreadedRenderer.draw(ThreadedRenderer.java:783)
42 android.view.ViewRootImpl.draw(ViewRootImpl.java:3194)

分析

表象是在View系统绘制ImageView的时候,ImageView的Bitmap被回收了,实际看不到具体崩溃在哪里,根据业务场景判断出可能是Glide造成的奔溃。
代码如下:

  Glide.with(activity).asBitmap().load(url)
    .into(object : CustomViewTarget<ImageView, Bitmap>(targetView) {
        override fun onLoadFailed(errorDrawable: Drawable?) {}
        override fun onResourceCleared(placeholder: Drawable?) {}
        override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
            targetView.setImageBitmap(resource)
        }

因此有两个猜想:

  1. onResourceReady给出的bitmap是已经被回收的。
  2. onResourceReady给出的bitmap是正常的,将Bitmap设置给ImageView之后,Glide又回收了该Bitmap。

Glide是一个非常通用的组件,猜想1应该不成立,就围绕猜想2进行分析Glide源代码。

原因说明

  • 问题直接原因:ImageView的图片被回收,在绘制ImageView的时候奔溃

  • 分析(一句话总结:新图片资源还没来,旧图片资源已被回收):
    Glide内存的缓存有几个: Active,Cache,Pool。
    当调用Glide的into的时候,当前的Target(包含Bitmap)进入Active,前一个Target(包含Bitmap)会从Active进入Cache。
    当内存紧张或者主动调用Glide.clearMemory时,会先执行Cache.clearMemory,会同步将Cache中的Target会进入Pool。然后会执行Pool.clearMemory,这时会出现调用Bitmap.recycle()。
    如果完成了clearMemory,而当前请求的图片资源因为网络速度慢或者被暂停,那么当前ImageView中的图片还是旧图片,如果发生重绘,就会因图片被回收而崩溃。

  • 我所遇到的情况:
    A界面使用Glide定时加载不同图片到ImageView中进行展示,这时跳转到B界面,Glide当前的Target会被clear,也会停止正在请求的图片(跟随了Activity生命周期)。
    在B界面发生了内存不足,发生了onLowMemory调用,由此引发上前面说明的Glide缓存回收的过程,图片被recycle,也就是A界面的ImageView的Bitmap被回收了,因为此时Activity处于onStop状态,不会发现绘制,所以不会有问题。
    但从预览界面再回来,首先会发生重绘,而此时ImageView的图片已经被回收,从而引发崩溃。
    除因为跳转到其他界面会发生这个问题外,如果在A界面网络很慢,也会发生该问题。

解决办法

办法1:在onResourceCleared给ImageView设置默认图片或者null.
办法2:直接使用into(ImageView)代替into(Target),如果需要在图片加载成功做一些事情,可以配合Listener来做。

关键点

Glide在内存紧张时候的回收行为会造成图片被回收。
Glide会随着Activity的stop会停止执行图片加载,在重新回到Activity的时候,会继续加载之前的请求。
新图片还未请求到,而ImageView中的旧图片已经被回收,ImageView绘制过程中崩溃。

结束

Glide的官方文档也有说明,在onResourceCleared的时候需要进行清理。

<think>好的,我现在需要分析用户提出的关于Android中setImageBitmap方法无法成功设置Bitmap的原因。用户提到引用了五个参考资料,我需要先仔细查看这些引用内容,找出可能相关的信息。 首先,用户的问题集中在setImageBitmap没有成功显示图片,可能的原因有很多。根据引用[1],里面提到在使用Bitmap.createBitmap时,如果没有执行某些代码,可能会导致在Canvas绘制时看不到图像。这可能涉及到Bitmap配置的问题,比如颜色模式不正确,或者没有正确初始化Bitmap。 引用[2]提到了NullPointerException,当调用bitmap.getWidth()时出现空指针。这可能意味着传入的Bitmap对象本身是null,或者在创建Bitmap时出现了错误,导致后续获取宽高失败。如果setImageBitmap传入的是null或者无效的Bitmap,自然无法显示。 引用[3]和[4]都讨论了Glide在使用过程中可能出现的Bitmap回收问题。特别是当Glide在内存不足时回收了Bitmap,如果此时再尝试使用该Bitmap设置到ImageView,就会抛出异常,导致显示失败。用户可能在异步加载图片后,没有正确处理Bitmap的生命周期,或者使用了已回收的Bitmap。 引用[5]提到Drawable是表现绘图结果的接口,BitmapDrawable是其中的一种。可能涉及到Bitmap到Drawable的转换问题,或者ImageView的ScaleType设置不正确,导致图片无法正确显示。 接下来,我需要综合这些信息,整理出可能的原因: 1. **Bitmap未正确初始化或配置错误**:比如使用createBitmap时参数不正确,或者颜色配置不匹配,导致生成的Bitmap无法渲染。 2. **Bitmap已被回收**:特别是在使用Glide等库时,如果Bitmap被回收后仍尝试使用,会导致无法显示,并可能引发异常。 3. **线程问题**:在非UI线程更新ImageView,或者异步加载图片后未在主线程设置Bitmap。 4. **空指针问题**:传入的Bitmap为null,或获取Bitmap的宽高时出现异常。 5. **硬件加速兼容性问题**:某些Bitmap配置可能与设备的硬件加速不兼容,导致无法渲染。 6. **ScaleType或ImageView尺寸问题**:如果ImageView的尺寸为0,或者ScaleType设置不当,即使Bitmap正确,也可能无法显示。 还需要考虑用户的具体使用场景,比如是否在自定义View中使用了setImageBitmap,或者是否有其他库的影响。比如引用[3]中的CustomViewTarget可能导致资源回收问题,而引用[4]提到使用ViewTarget可能引发异常,而推荐直接使用ImageView。 此外,要确保Bitmap没有被意外回收,比如在Glide的Target中正确管理资源。例如,在onResourceReady中及时设置Bitmap,并且避免在之后再次使用已经被Glide回收的Bitmap对象。 可能还需要检查内存泄漏或过度回收的情况,比如频繁创建大图导致内存不足,触发系统回收,进而引发问题。 总结起来,需要从Bitmap的创建、生命周期管理、线程安全、UI更新以及第三方库的使用等多个方面进行排查。</think>### Android中setImageBitmap无法成功设置Bitmap的原因分析 #### 一、Bitmap对象本身无效或已回收 1. **Bitmap未正确初始化** - 使用$Bitmap.createBitmap()$时若未正确设置宽高或颜色配置(如$Bitmap.Config$),可能导致生成的Bitmap无法渲染[^1]。例如: ```java Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); //正确配置 ``` - 若未执行Canvas绘图操作,可能导致生成的Bitmap为空图像[^1]。 2. **Bitmap已被回收** - 使用Glide等库时,若Bitmap被回收后仍尝试设置到ImageView,会抛出`Canvas: trying to use a recycled bitmap`异常[^4]。常见于以下场景: ```kotlin Glide.with(context).asBitmap().load(url).into(object : CustomViewTarget<ImageView, Bitmap>() { override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) { targetView.setImageBitmap(resource) // 后续若再次使用resource对象,可能已被Glide回收 } }) ``` #### 二、线程与生命周期问题 1. **非UI线程更新ImageView** - 在子线程中直接调用$setImageBitmap()$会导致UI更新失败。应通过$runOnUiThread()$或Handler切换至主线程。 2. **异步加载未完成时操作Bitmap** - 若在Glide的`onResourceReady`回调外部提前使用Bitmap对象,可能因异步加载未完成而获取到null[^3]。 #### 三、硬件加速与配置兼容性 1. **硬件加速限制** - 某些Bitmap配置(如$Bitmap.Config.RGB_565$)可能因设备硬件加速限制无法渲染,需验证颜色模式是否支持。 2. **View尺寸为0** - 若ImageView的宽高未正确测量(如未完成布局流程),即使设置Bitmap也无法显示。可通过`post()`延迟操作: ```java imageView.post(() -> imageView.setImageBitmap(bitmap)); ``` #### 四、第三方库使用规范 1. **Glide的Target类型选择** - 避免使用自定义$ViewTarget$,优先使用标准`into(ImageView)`方法,可自动处理Bitmap生命周期[^4]。 - 错误示例: ```kotlin Glide.with(context).load(url).into(ViewTarget(imageView)) // 可能导致资源回收异常 ``` 2. **Bitmap回收策略** - 若需手动管理Bitmap,应禁用GlideBitmapPool缓存: ```java RequestOptions options = new RequestOptions().skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE); Glide.with(context).load(url).apply(options).into(imageView); ``` #### 五、调试与验证方法 1. **基础检查** - 验证Bitmap是否为null:`if (bitmap == null || bitmap.isRecycled())`。 - 检查ImageView可见性:`imageView.getVisibility() == View.VISIBLE`。 2. **日志与异常捕获** - 捕获可能的异常: ```java try { imageView.setImageBitmap(bitmap); } catch (RuntimeException e) { Log.e("TAG", "Bitmap状态异常: " + e.getMessage()); } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值