TransitionDrawable使用不当导致内存泄露
2015-07-12 00:42
681 查看
最近要做类似网易云音乐背景高斯模糊的效果, 同时也想让背景变化时不要那么生硬, 就是下面这个效果
Google一番后决定用TransitionDrawable, 由于是配合UniversalImageLoader使用, 所以只需要实现一个BitmapDisplayer作为UIL的配置项就行了.
最初的代码是这样写的
最关键的部分是
简单明了. 实际使用中, 发现app占用的内存越来越高, 但只要退出
问题出在这句代码
乍一看这句代码逻辑是没有问题的, 每次我们都是将旧的
程序进行第一次渐变动画后,
简单示意一下
第一次渐变后:
第二次渐变后:
第三次渐变后:
这样
所以我们正确的做法不应该是直接将
当然另一种思路是
最终代码修改成下面的样子, 主要是需要判断
Google一番后决定用TransitionDrawable, 由于是配合UniversalImageLoader使用, 所以只需要实现一个BitmapDisplayer作为UIL的配置项就行了.
最初的代码是这样写的
private static class DrawableFadeDisplayer implements BitmapDisplayer { private final int durationMillis; public DrawableFadeDisplayer(int durationMillis) { this.durationMillis = durationMillis; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { ImageView imageview = (ImageView) imageAware.getWrappedView(); Drawable oldDrawable = imageview.getDrawable(); TransitionDrawable td = new TransitionDrawable(new Drawable[] { oldDrawable==null?(new ColorDrawable(Color.TRANSPARENT)):oldDrawable, new BitmapDrawable(Resources.getSystem(), bitmap) }); imageview.setImageDrawable(td); td.startTransition(durationMillis); } }
最关键的部分是
display中的代码, 首先获取了旧的
Drawable, 然后和新生成的
BitmapDrawable一起构造一个
TransitionDrawable, 最后调用
startTransition就可以了.
简单明了. 实际使用中, 发现app占用的内存越来越高, 但只要退出
Activity, 一两次GC之后内存就会降下来, 基本可以确定是这段代码造成了内存泄露.
问题出在这句代码
Drawable oldDrawable = imageview.getDrawable();
乍一看这句代码逻辑是没有问题的, 每次我们都是将旧的
Drawable作为第一层, 新的
Drawable作为第二层创建
TransitionDrawable, 但是注意我们是创建的
TransitionDrawable, 并将它设给
ImageView, 也就是说我们调用
getDrawable拿到的也是
TransitionDrawable, 一个
TransitionDrawable其实是持有多个
Drawable的, 在这里是持有两个.
程序进行第一次渐变动画后,
ImageView中的
TransitionDrawable持有两个
Drawable, 第二次渐变动画, 我们将
TransitionDrawable和新的
BitmapDrawable组合在一起创建一个新的
TransitionDrawable.
简单示意一下
ImageView持有的
Drawable:
第一次渐变后:
TransitionDrawable(drawable0, drawable1)
第二次渐变后:
TransitionDrawable( TransitionDrawable(drawable0, drawable1), drawable2 )
第三次渐变后:
TransitionDrawable(
TransitionDrawable( TransitionDrawable(drawable0, drawable1), drawable2 ),
drawable3
)
这样
ImageView导致不能被回收的
Drawable数量越来越多, 最终OOM.
所以我们正确的做法不应该是直接将
getDrawable的值拿来当第一层
Drawable, 而是先判断一下这个值的类型, 如果是
TransitionDrawable, 应该获取它第二层
Drawable作为我们的第一层, 这样原来的第一层
Drawable就会失去到GC Roots的引用链, 从而可以被回收.
当然另一种思路是
TransitionDrawable动画完成之后再将新的
BitmapDrawable设给
ImageView, 但并没有这个监听器, 最简单便捷的还是上面的思路.
最终代码修改成下面的样子, 主要是需要判断
getDrawable的类型, 如果是
TransitionDrawable, 就获取第二层
Drawable.
private static class DrawableFadeDisplayer implements BitmapDisplayer { private final int durationMillis; public DrawableFadeDisplayer(int durationMillis) { this.durationMillis = durationMillis; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { ImageView imageview = (ImageView) imageAware.getWrappedView(); Drawable oldDrawable = imageview.getDrawable(); Drawable oldBitmapDrawable = null; if (oldDrawable == null) { oldBitmapDrawable = new ColorDrawable(Color.TRANSPARENT); } else if (oldDrawable instanceof TransitionDrawable) { oldBitmapDrawable = ((TransitionDrawable) oldDrawable).getDrawable(1); } else { oldBitmapDrawable = oldDrawable; } TransitionDrawable td = new TransitionDrawable(new Drawable[] { oldBitmapDrawable, new BitmapDrawable(Resources.getSystem(), bitmap) }); imageview.setImageDrawable(td); td.startTransition(durationMillis); } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories