drawable.setCallback(null)的后果是什么?

16

尝试实现小型内存缓存Drawables时,我了解到在关闭活动后避免内存泄漏需要解绑这些Drawables:将它们的回调设置为null。

因为在每个活动中维护缓存在内存中的Drawables需要额外的代码,所以我尝试在setImageDrawable(drawable)之后立即解绑它们,至今没有看到任何影响。
这是MyImageView类的代码(extends ImageView):

setImageDrawable(drawable);
d.setCallback(null);

在调试器中,我可以清楚地看到在第一行代码之前回调为null,在第一行代码之后,它被设置为此imageView,然后我又将其设置为null。之后它通常会显示。

setCallback(Drawable.Callback cb)的文档说明如下:

将一个Drawable.Callback对象绑定到此Drawable上。对于希望支持动画Drawable的客户端是必需的。

既然我不需要动画Drawable,我看不出为什么我不应该这样做,但是在有关Android中关于Drawable的内存泄漏的几个博客中,这只是在活动结束后才执行的。问题是,为什么当绑定到ImageView时,回调总是自动设置?

在那些将回调设置为null的Drawable中是否存在某些边界条件会导致问题?比如无法显示或NPE错误?

1个回答

19

不应该对Drawable对象进行缓存——Drawable对象具有非常明显的状态,旨在仅由一个所有者使用。

如果要实现缓存,应该对可绘制对象的常量状态进行缓存。

可以通过以下方式检索常量状态:

http://developer.android.com/reference/android/graphics/drawable/Drawable.html#getConstantState()

(请注意,此方法可能返回null;并非所有的Drawables都有常量状态。)

稍后可以使用以下方法从常量状态实例化新的Drawable对象:

http://developer.android.com/reference/android/graphics/drawable/Drawable.ConstantState.html#newDrawable(android.content.res.Resources)

同时需要注意的是,Resources已经为您维护了一组Drawables的缓存,使用此功能,因此无需为从Resources检索到的任何Drawable实例实现自己的缓存。

如果正在创建自己的Drawables,并且这些Drawables不是来自资源,则强烈建议先对基础数据(例如从网络下载的位图)进行缓存,然后再尝试更改常量状态。(并且绝对不要缓存Drawable对象本身。)


这是一个从网络下载的位图制作的可绘制对象缓存,在ListView中显示。那么您建议缓存实际的位图吗?从位图创建Drawable是否存在任何重大的性能开销? - CloudWalker
我应该使用setImageBitmap而不是setImageDrawable。 - CloudWalker
4
好的,我会尽力进行翻译:确保缓存位图。与加载位图相比,绘制对象的成本非常低廉。它只是引用了位图。 - hackbod
抱歉重新提出这个问题,但我不确定它是否完全回答了问题...如果不需要动画,早期执行SetCallback Null而不是修改是否安全?我必须使用可绘制对象进行列表对象选择,但我相信回调会导致对mContext的泄漏。 - sradforth
1
@hackbod 我有一个静态的ConcurrentHashMap缓存对象和drawable,drawable是已安装的应用程序图标,我在适配器的bindView()方法中使用bitmap和setImageBitmap为imageView设置图像,但是setImageBitmap将调用setImageDrawable(new BitmapDrawable(Bitmap)),这会在滚动时产生大量垃圾,因为始终会创建新的Bitmap drawable对象,所以我选择了Drawable缓存。你能否建议我更好的方法来拥有静态的drawable缓存或位图缓存,而不会产生大量的gc_alloc? - om252345

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接