标签:
移动设备不断发展的今天,有的人认为内存已经足够大了,不用再管什么内存优化,Java是虚拟机可以帮我维护内存。其实内存空间资源还是很宝贵的,不管手机内存有多大,系统分配给单个应用的内存空间还是很有限的大致有16M,64M,128M等。在Android中加载大图会非常消耗系统资源,16M的图片大致可以存储3张1024X1536质量为ARGB_8888的图片,这里边还不包含其它Object所占的资源。软件在系统上运行,环境是很复杂的,可能测试的时候有限的测试次数上没有发现内存泄漏问题,但是在成千上万的用户使用过程中,总会有各种内存泄露问题。
大图加载首先要清楚图片的质量与所占内存的计算规则:Bitmap.Config
Bitmap.Config |
介绍(每个像素点的构成) |
1pix所占空间 1byte = 8位 |
1024*1024图片大小 (分辨率) |
ALPHA_8 |
只有透明度,没有颜色,那么一个像素点占8位。 |
1byte |
1M |
RGB_565 |
即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位 |
2byte |
2M |
ARGB_8888 |
由4个8位组成,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位 |
4byte |
4M |
ARGB_4444 |
由4个4位组成,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 |
2byte |
2M |
解码的时候设置Bitmap.inPreferredConfig ,尝试加载不同质量的位图到内存;
就是先获取将要加载的图片的大小,预计算内存的占用和是否我们需要这样分辨率的图片。
采用BitmapFactory加载图片,它提供了decodeResource 和decodeFile两个方法给我们解码Resource目录和本地SDCard目录下的图片,提供了强大的BitmapFactory.Options 给我在解码图片的过程中的配置。
图片分辨率为:8176 x 2368 颜色模型是:ARGB_8888, 完全加载到内存所需资源是:73.6M左右,意味着巨大多数的Android手机都会瞬间内存泄漏。
加载图片时先获取图片的大小和图片的格式:将inJustDecodeBounds设置为true,不解码图片到内存,只读取图片的基本信息。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
这时候可以获取到图片的大小和格式,然后根据需要加载的模型计算所占内存空间的大小:
/** * A helper function to return the byte usage per pixel of a bitmap based on its configuration. */ public static int getBytesPerPixel(Bitmap.Config config) { if (config == Bitmap.Config.ARGB_8888) { return 4; } else if (config == Bitmap.Config.RGB_565) { return 2; } else if (config == Bitmap.Config.ARGB_4444) { return 2; } else if (config == Bitmap.Config.ALPHA_8) { return 1; } return 1; }
/** * get the image size in the RAM * * @param imageW * @param imageH * @return */ public static long getBitmapSizeInMemory(int imageW, int imageH) { return imageH * imageW * getBytesPerPixel(Bitmap.Config.ARGB_8888); }
/** * Load bitmap from resources * * @param res resource * @param drawableId resource image id * @param imgH destination image height * @param imgW destination image width * @return */ public static Bitmap loadHugeBitmapFromDrawable(Resources resources, int drawableId, int imgH, int imgW) { Log.d(TAG, "imgH:" + imgH + " imgW:" + imgW); BitmapFactory.Options options = new BitmapFactory.Options(); //preload set inJustDecodeBounds true, this will load bitmap into memory options.inJustDecodeBounds = true; //options.inPreferredConfig = Bitmap.Config.ARGB_8888;//default is Bitmap.Config.ARGB_8888 BitmapFactory.decodeResource(resources, drawableId, options); //get the image information include: height and width int height = options.outHeight; int width = options.outWidth; String mimeType = options.outMimeType; Log.d(TAG, "width:" + width + " height:" + height + " mimeType:" + mimeType); //get sample size int sampleSize = getScaleInSampleSize(width, height, imgW, imgH); options.inSampleSize = sampleSize; // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; Log.d(TAG, "memory size:" + getBitmapSizeInMemory(width / sampleSize, height / sampleSize)); Bitmap bitmap = BitmapFactory.decodeResource(resources, drawableId, options); Log.d(TAG, "w=" + bitmap.getWidth() + " h=" + bitmap.getHeight() + " bitmap size:" + bitmap.getRowBytes() * bitmap.getHeight()); return bitmap; }
/** * load the bitmap from SDCard with the imgW and imgH * * @param imgPath resource path * @param imgH result image height * @param imgW result image width * @return result bitmap */ public static Bitmap loadHugeBitmapFromSDCard(String imgPath, int imgH, int imgW) { Log.d(TAG, "imgH:" + imgH + " imgW:" + imgW); BitmapFactory.Options options = new BitmapFactory.Options(); //preload set inJustDecodeBounds true, this will load bitmap into memory options.inJustDecodeBounds = true; //options.inPreferredConfig = Bitmap.Config.ARGB_8888;//default is Bitmap.Config.ARGB_8888 BitmapFactory.decodeFile(imgPath, options); //get the image information include: height and width int height = options.outHeight; int width = options.outWidth; String mimeType = options.outMimeType; Log.d(TAG, "width:" + width + " height:" + height + " mimeType:" + mimeType); //get sample size int sampleSize = getScaleInSampleSize(width, height, imgW, imgH); options.inSampleSize = sampleSize; // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; Log.d(TAG, "memory size:" + getBitmapSizeInMemory(width / sampleSize, height / sampleSize)); Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options); Log.d(TAG, "w=" + bitmap.getWidth() + " h=" + bitmap.getHeight() + " bitmap size:" + bitmap.getRowBytes() * bitmap.getHeight()); return bitmap; }
两种方式的原理完全一样。
根据官方文档我们可以了解到,inSampleSize必须是2的指数倍,如果不是2的指数倍会自动转位教下的2的指数倍的数,下边我提供一个计算方法:
/** * get the scale sample size * * @param resW resource width * @param resH resource height * @param desW result width * @param desH result height * @return */ public static int getScaleInSampleSize(int resW, int resH, int desW, int desH) { int scaleW = resW / desW; int scaleH = resH / desH; int largeScale = scaleH > scaleW ? scaleH : scaleW; int sampleSize = 1; while (sampleSize < largeScale) { sampleSize *= 2; } Log.d(TAG, "sampleSize:" + sampleSize); return sampleSize; }
接下来,我讲继续讲解:图片加载之图片缓存和异步加载技术。
最后我附上该项目的github地址:https://github.com/CJstar/HugeLocalImageLoad.git
参考:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
版权声明:本文为博主原创文章,未经博主允许不得转载。
Android大图加载内存优化(如何防止OutOfMemory)
标签:
原文地址:http://blog.csdn.net/cj_star/article/details/47833345