1、将图片转化为缩略图再加载:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap img = BitmapFactory.decodeFile("/sdcard/1.png", options);
该段代码即是读取1.png的缩略图,长度、宽度都只有原图片的1/2。图片大小减少,占用的内存自然也变小了。这么做的弊端是图片质量变差,inSampleSize的值越大,图片的质量就越差。由于各手机厂商缩放图片的算法不同,在不同手机上的缩放图片质量可能会不同。笔者就遭遇过moto手机上图片缩放后质量可以接受,三星手机上同样的缩放比例,质量却差很多的情况。
2、用ARBG_4444颜色模式加载图片:
Android中有四种,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存
RGB_565:每个像素占用2byte内存
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.ARGB_4444; Bitmap img = BitmapFactory.decodeFile("/sdcard/1.png", options);
以上代码即是将1.png以ARGB_4444模式读出。内存减少虽然不如第一种方法明显,但是对于大多数图片,看不出与ARGB_8888模式有什么差别。不过在读取有渐变效果的图片时,可能有颜色条出现。另外,会影响图片的特效处理。
3、调用图片的recycle()方法:
这个其实不是真正降低图片内存的方法。主要目的是标记图片对象,方便回收图片对象的本地数据。图片对象的本地数据占用的内存最大,而且与程序Java部分的内存是分开计算的。所以经常出现Java heap足够使用,而图片发生OutOfMemoryError的情况。在图片不使用时调用该方法,可以有效降低图片本地数据的峰值,从而减少OutOfMemoryError的概率。不过调用了recycle()的图片对象处于“废弃”状态,调用时会造成程序错误。所以在无法保证该图片对象绝对不会被再次调用的情况下,不建议使用该方法。特别要注意已经用setImageBitmap(Bitmap img)方法分配给控件的图片对象,可能会被系统类库调用,造成程序错误。
例如在小米手机上:
Bitmap old = img; img = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true); old.recycle();
这段代码会发生Bitmap对象已经被recycle的错误。在三星、moto、HTC、华为和其他一些厂商的机器上这段代码运行正常。虽然是厂商定制的系统实现机制不同造成的问题。但确实是需要注意的问题。 4、使用Matrix对象放大的图片如何更改颜色模式: 虽然使用Matrix对象放大图片,必定会耗费更多的内存,但有时候也不得不这样做。放大后的图片使用的ARGB_8888颜色模式,就算原图片是ARGB_4444颜色模式也一样,而且没有办法在放大时直接指定颜色模式。可以采用以下办法更改图片颜色模式。
Matrix matrix = new Matrix(); float newWidth = 200;//图片放大后的宽度 float newHeight = 300;//图片放大后的长度 matrix.postScale(newWidth / img.getWidth(), newHeight/ img.getHeight()); Bitmap img1 = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);//得到放大的图片 img2 = img1.copy(Bitmap.Config.ARGB_4444, false);//得到ARGB_4444颜色模式的图片 img = null; img1 = null;
这里比起原来的图片额外生成了一个图片对象img1。但是系统会自动回收img1,所以实际内存还是减少了。
归结起来还是以缩略图模式读取图片和减少图片中每个像素占用的内存。这两种方法虽然有效,但是也有各自的弊端。实际开发中还是应该根据情况酌情使用。最王道的办法,还是避免垃圾对象的产生。例如在ListView的使用中,复用convertView等。如果使用AsyncTask加载图片,要及时将引用的ImageView对象置为null。因为AsyncTask是用线程池实现的,所以其中引用的对象可能会拥有很长的生命周期,造成GC无法释放。我还是相信Android的内存回收机制的,recycle什么的虽然一定程度上有效,但总觉得不符合Java内存回收的原则。(最后这句完全是着魔了)