1. 内存管理基础知识
http://www.cnblogs.com/xingfuzzhd/p/3485924.html
- 1.
mImageView.setImageResource(R.drawable.my_image);
这段代码会调用 BitmapFactory.decodeStream() 生成一个 Bitmap。所以不要以为它比自己创建 Bitmap 节省内存。
3. 实际测试:
我使用了多种调用图片的方法来测试:
第一种:
// 直接载入资源 id
ImageView image = (ImageView) findViewById(R.id.imageView1)
第二种:
创建一个函数,根据 id 载入图片。杀死 Activity 时回收 Bitmap
public BitmapDrawable getImageBitmap(int idImage) {
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), idImage);
images.add(bitmap);
return new BitmapDrawable(context.getResources(), bitmap);
}
第三种:
创建一个类
/**
* solution for OOM ------> compress bitmap
*/
public class BitmapUtils {
private static final String TAG = "BitmapUtils";
/**
* calulate the compress rate
*/
private static int calculateInSampleSize(BitmapFactory.Options opts,int reqHeight,int reqWidth) {
if(opts == null) {
return -1;
}
int width = opts.outWidth;
int height = opts.outHeight; int sampleSize = 1; if(width > reqWidth || height > reqHeight) {
int heightRatio = (int) (height/(float)reqHeight);
int widthRatio = (int) (width/(float)reqWidth);
sampleSize = (heightRatio > widthRatio) ? widthRatio : heightRatio;
}
return sampleSize;
} /**
* compress an image refer to the goal dimension
* @param res
* @param resId
* @param reqWidth
* @param reqHeight
* @return Bitmap
*/
public static Bitmap decodeSampledBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, opts);
int sampleSize = calculateInSampleSize(opts, reqHeight, reqWidth);
Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]");
opts.inJustDecodeBounds = false;
opts.inSampleSize = sampleSize;
/* newly added */
opts.inPreferredConfig = Bitmap.Config.RGB_565;
opts.inPurgeable = true;
opts.inInputShareable = true;
/* newly added */
Log.i(TAG,"insamplesize="+sampleSize);
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, opts);
Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]");
return bitmap;
} /**
* compress an image refer to the goal dimension
*
* @param data image byte array
* @param reqWidth
* @param reqHeight
* @return
*/
public static Bitmap decodeSampledBitmapFromByteArray(byte[] data,int reqWidth,int reqHeight) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, opts);
Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]");
opts.inSampleSize = calculateInSampleSize(opts, reqHeight, reqWidth);
Log.i(TAG,"insamplesize="+opts.inSampleSize);
opts.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]");
return bitmap;
} public static Bitmap decodeResourceWithLowMemory(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
// Bitmap.Config.ALPHA_8,Bitmap.Config.ARGB_4444,Bitmap.Config.RGB_565
// 设置这几个参数效果都差不多,确实相当省内存啊,而且还跟原图清晰度相当
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
return BitmapFactory.decodeResource(context.getResources(), resId, opt);
} public static Bitmap loadToImageViewFromResource(Resources res,int resId,ImageView imageView) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, opts);
int reqHeight = imageView.getLayoutParams().height;
int reqWidth = imageView.getLayoutParams().width;
int sampleSize = calculateInSampleSize(opts, reqHeight, reqWidth);
Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]");
opts.inJustDecodeBounds = false;
opts.inSampleSize = sampleSize;
/* newly added */
opts.inPreferredConfig = Bitmap.Config.RGB_565;
opts.inPurgeable = true;
opts.inInputShareable = true;
/* newly added */
Log.i(TAG,"insamplesize="+sampleSize);
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, opts);
Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]");
return bitmap;
} }
4. 测试过程:
创建两个 Activity,每个都载入几个大图片,然后两个 Activity 之间不停的切换。
第一种方法,切换多次后 OOM (out of memory)。 第二种方法,更快的 OOM。
第三种,如果压缩率够高的话,几乎不会 OOM。比如原始图片为 2048 * 1600,然后目标 ImageView 的大小为 200* 150。最后内存消耗将大大减小。然后如果
两者分辨率差距不大,最后内存消耗将不会被优化,最后也将 OOM。
5. 原理
通常,我们使用BitmapFactory.decodeResource()方法来从资源文件中读取一张 图片并生成一个 Bitmap。但如果使用一个BitmapFactory.Options对象,并把该对象的inJustDecodeBounds属性设 置为true,decodeResource()方法就不会生成 Bitmap 对象,而仅仅是读取该图片的尺寸和类型信息。所以在前面的类的一些方法中,我们用 BitmapFactory decode了两次图片,但第一次只是获取信息而没有生成 Bitmap 对象,所以没有未回收的 Bitmap 出现。
6. 一些推荐的解决方案
弱引用,LruCache,或者开源项目 :Android-Universal-Image-Loader
延伸:一个Bitmap到底占用多大内存?系统给每个应用程序分配多大内存?
· Bitmap占用的内存为:像素总数 * 每个像素占用的内存。在Android中,Bitmap有四种像素类型:ARGB_8888、ARGB_4444、ARGB_565、ALPHA_8,他 们每个像素占用的字节数分别为4、2、2、1。因此,一个2000*1000的ARGB_8888类型的Bitmap占用的内存为 2000*1000*4=8000000B=8MB。
· Android根据设备屏幕尺寸和dpi的不同,给系统分配的单应用程序内存大小也不同,具体如下表(表格取自Android 4.4 Compatibility Definition Document (CDD)):
屏幕尺寸 | DPI | 应用内存 |
small / normal / large | ldpi / mdpi | 16MB |
small / normal / large | tvdpi / hdpi | 32MB |
small / normal / large | xhdpi | 64MB |
small / normal / large | 400dpi | 96MB |
small / normal / large | xxhdpi | 128MB |
xlarge | mdpi | 32MB |
xlarge | tvdpi / hdpi | 64MB |
xlarge | xhdpi | 128MB |
xlarge | 400dpi | 192MB |
xlarge | xxhdpi | 256MB |