我们在开发的时候经常会遇到很多关于bitmap回收的问题,如果不回收不断的创建重复对象的时候导致内存泄漏。如果回收由于时机不对导致加载已回收的bitmap报错。那么小鱼在这里就几个具体的问题引导一下新手们怎么选择回收时机。
1.选择合适的图片框架,统一管理。但是这个并不能完全规避回收问题。因为我们的图片来源很广,很多时候并不想加入缓存。甚至我们有时候写框架或者自定义视图给别人用的时候,我们不能说别人一定会用到图片管理框架,就不处理等等。
2.首先要记住,一定要在主线程回收。当你发现自己回收图片在子线程的时候,请注意你是否是线成安全或者加锁。因为我们的视图在主线程加载的,所以当imageview等加载缓存视图的时候保证和回收动作不重合。
3.当我们自定义视图的时候,在绘制bitmap的时候,需要怎么处理?
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
synchronized (CustomHelpView.this) {
canvas.drawBitmap(mQrCode, 0,0, mPaint);
}
}
public synchronized void setQrCode(Bitmap bitmap) {
if (mQrCode != null) {
mQrCode.recycle();
}
mQrCode = bitmap;
}
3.1一个简单的同步锁。我们在获取新bitmap的时候将老的bitmap回收。同时保证在回收的时候不会加入已经回收的图片。
4.在子线程获取文件到主线程使用。
子线程:public void autoGetHelpInfor() {
mHelp = new HelpInfor();
UtilsFile.getHelpInfor(mHelp);
}
通过handler消息回到主线程:
private HelpInfor targetHelp;
private void handleUIUpdateHelp() {
if (targetHelp != null && targetHelp.getHelpBmp() != null) {
targetHelp.getHelpBmp().recycle();
}
targetHelp = mDataFactory.getmHelp();
mDataInterface.notifyHelp();
}
4.1这个是我在工厂模式下,对图片文件信息的操作。很多人觉得有必要这么麻烦?直接每次在获取帮助信息的时候先回收图片再获取使用就行了,反正handler通知主线程更新了。但是大家往往会忽略一个问题,就是我们的控件自己会对图片缓存,方便下次直接获取使用。我们如果在子线程销毁的图片,这时候主线程正在从控件缓存加载原来的图片怎么办?当然,报错,使用了已经回收的图片。所以我们新创建一个对象装载我们新的图片。当回到主线程操作的时候,将我们原来的图片回收,然后再将新的对象赋给我们的目标对象。这里需要注意的是,从头到尾我们发送出去的都是新对象,而目标对象只是为了引用我们的老图片能对其做回收操作。很多人会觉得这种写法很奇怪。没事这种一般是在框架里面,用的人直接使用就行了,是为了防止用的人不用或者用了不回收,框架里面手动回收。
5.延迟回收:当我们在一个列表中有些图片是需要的,有些图片是不需要的,这个时候我们需要回收不需要的图片的时候,我们也可以采用这种偷懒模式,handler加延迟对我们的列表中不需要的图片进行图片回收。一般会设置几秒钟的时间回收。因为我们可以保证这些图片会在几秒钟之内加载完成,也就是说等图片加载完成后再回收。虽然这种方式有点low甚至很不精确。但是对于很多技术菜一点的新手来说很适用。
6.最简单直接的办法:
if (mIvHelp.getDrawingCache() != null) mIvHelp.getDrawingCache().recycle();
mIvHelp.setImageBitmap(bitmap);
6.1当我们需要反复对某个控件添加图片的时候,直接一针见血。
7.hashmap或set等不重复的集合存放bitmap时一定要注意put的时候要把老的图片回收了,否则内存泄漏。
8.使用LruCache管理图片缓存,自己写一个图片简单缓存工具。
9.bitmap转换的时候(最骚操作),比如Bitmap newBmp = Bitmap.createBitmap(oldBmp,0,0,100,100); 很多新手懂不起转换原理而错误的写法,甚至不知道报错的原因,觉得这时候不需要回收,系统会自动帮你回收,干脆就不回收了。如下:
if(oldBmp != null){oldBmp.recycle(); oldBmp = null; }
实际上该方法如果在不需要剪切的情况下会返回oldBmp也就是说newBmp有可能等于oldBMp。所以应该这么写:
if(newBmp != oldBmp ){oldBmp.recycle(); oldBmp = null; }这里不需要判空,应该在你转换之前就要保证oldbmp不能为空。