标签:
Bitmap所占用内存
Android的虚拟机是基于寄存器的Dalvik,它的最大堆(单个进程可用内存)大小一般是16M,当然不同设备是不一样的,可以查看/system/build.prop文件,[注:现在的机子可能一般都有160M左右]。程序本身运行就占有一定的内存,在使用较大的bitmap时,由于Bitmap自身的特性(将每个像素的属性全部保存在内存中),导致稍有不慎就会创建出一个占用内存非常大的Bitmap对象,从而导致加载过慢,还会有内存溢出的风险。所以,加载Bitmap时对其进行优化是必不可少的一步。例如:
mImageView.setImageResource(R.drawable.my_image); 这是一行从资源文件中加载图片到ImageView的代码。实际上,以上这行代码会在运行时使用BitmapFactory.decodeStream()方法将资源图片生成一个Bitmap,然后由这个Bitmap生成一个Drawable,最后再将这个Drawable设置到ImageView。由于在过程中生成了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。
注意:Bitmap占用内存大小和硬盘中的.png图片的大小没有一毛钱关系!
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 64MBsmall / normal / large 400dpi 96MB
small / normal / large xxhdpi 128MBxlarge mdpi 32MBxlarge tvdpi / hdpi 64MBxlarge xhdpi 128MBxlarge 400dpi 192MBxlarge xxhdpi 256MB实际测试在720P的华为荣耀2和1080P的华为荣耀7上都能正常加载 5016*7891*4/1024/1024 = 150M 的图片Log.i("bqt", bitmap.getWidth() * bitmap.getHeight() * 4 / 1024 / 1024 + "-" + bitmap.getByteCount() / 1024 / 1024);//150-150
而对于 5612*8414*4/1024/1024 = 180M 的图片都加载失败了,但应用并没有挂掉,所以估计单个应用的内存被调到了160M。注意区分占用内存(宽*高*每个像素占用的内存)和图片像素(宽*高)的区别对于ARGB_8888来说,内存=像素*4
所以1000万像素占用内存近似为:10M*4=40M
BitmapFactory所有方法
public static Bitmap decodeByteArray(byte[] data, int offset, int length)public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)public static Bitmap decodeFile(String pathName)public static Bitmap decodeFile(String pathName, Options opts)public static Bitmap decodeFileDescriptor(FileDescriptor fd)public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)public static Bitmap decodeResource(Resources res, int id)public static Bitmap decodeResource(Resources res, int id, Options opts)public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, Options opts)public static Bitmap decodeStream(InputStream is)public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)以上所有重载方法,参数较少的方法都是直接调用参数较多的方法,调用时传入的其他参数为null。以上方法返回的Bitamp都是不可修改的。
BitmapFactory.Options
boolean inJustDecodeBounds——如果设置为true,不获取图片,不分配内存,但会返回图片的宽高信息int outWidth——获取图片的宽度值 int outHeight——获取图片的高度值int inSampleSize——图片缩放的倍数。如果设为4,则宽和高都为原来的1/4,则图是原来的1/16 int inDensity——用于位图的像素压缩比 int inTargetDensity——用于目标位图的像素压缩比(要生成的位图) boolean inScaled——设置为true时进行图片压缩,从inDensity到inTargetDensityboolean inPurgeable——设置图片是否可以被回收,创建Bitmap时用于存储像素的内存空间在系统内存不足时可以被回收boolean inInputShareable ——设置是否解码位图的尺寸信息boolean inDither——设置是否进行图片抖动处理Bitmap.Config inPreferredConfig——设置颜色格式,设为null可让解码器以最佳方式解码关于inSampleSize
该参数为int型,他的值指示了在解析图片为Bitmap时在长宽两个方向上像素缩小的倍数。inSampleSize的默认值和最小值为1,当小于1时,解码器将该值当做1来处理,且在大于1时,该值只能为2的幂(当不为2的幂时,解码器会取与该值最接近的2的幂)。例如,当inSampleSize为2时,一个2000*1000的图片,将被缩小为1000*500,相应地,它的像素数和内存占用都被缩小为了原来的1/4:
加载大图详细demo
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ImageView imageView = new ImageView(this);//通过设置不同的显示模式,可以观察压缩后的Bitmap到底多大imageView.setScaleType(ScaleType.CENTER);//按图片原来大小居中显示,若图片长/宽 > View的长/宽,则截取图片的居中部分显示//imageView.setScaleType(ScaleType.CENTER_INSIDE);//将图片完整居中显示,通过按比例缩小使得图片长/宽 <= View的长/宽//1.得到加载Bitmap的View(屏幕)的宽高,API13 android3.2 之后才能用Point point = new Point();getWindowManager().getDefaultDisplay().getSize(point);int screenWidth = point.x;int screenHeight = point.y;Log.i("bqt", "屏幕宽高:" + screenWidth + "-" + screenHeight);//屏幕宽高:1080-1794//2.得到原始图片的宽高BitmapFactory.Options options = new Options();//加载和显示图片是很消耗内存的,Options 类允许我们定义图片以何种方式读到内存options.inJustDecodeBounds = true;//不去解析真实的位图,只是获取这个位图的边界等信息。这是关键!Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath() + "/big_image.jpg", options);int bitmapWidth = options.outWidth;int bitmapHeight = options.outHeight;Log.i("bqt", "图片宽高:" + bitmapWidth + "-" + bitmapHeight + "-" + (null == bitmap));//3600-5400-true。返回的是null//3.计算缩放比例。这里为了后面的演示将scale设为了float,实际中全部使用int计算即可,因为后面用到的也是intfloat scaleX = 1.0f * bitmapWidth / screenWidth;float scaleY = 1.0f * bitmapHeight / screenHeight;float scale = Math.max(scaleX, scaleY);//以最大限度显示图片Log.i("bqt", "缩放前:" + scaleX + "-" + scaleY + "-" + scale);//3.3333333-3.0100334-3.3333333//4.缩放加载图片到内存//4.1.要想节约内存,必须使用 inSampleSize 这个成员变量//options.inSampleSize = (int) scale;//此值最终会被解析器解析成2的n次幂(1、2、4、8),表示图片宽高缩小到原来的几分之一options.inSampleSize =calculateInSampleSize(options, screenWidth, screenHeight);//4.2.使用下面两个成员变量并不会改变加载进内存的Bitmap的大小,所以根本用不着options.outWidth = (int) (bitmapWidth / scale);options.outHeight = (int) (bitmapHeight / scale);Log.i("bqt", "options中设置的宽高:" + options.outWidth + "-" + options.outHeight + "-" + options.inSampleSize);//1080-1620-3//4.3.下面这些参数都不是必须的options.inPurgeable = true;//建议加上。设置图片可以被回收,创建Bitmap时用于存储像素的内存空间在系统内存不足时可以被回收options.inInputShareable = true;//建议加上。设置解码位图的尺寸信息options.inDither = false; //不进行图片抖动处理options.inPreferredConfig = null; //让解码器以最佳方式解码//4.4.真正的去解析这个位图。options.inJustDecodeBounds = false;bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath() + "/big_image.jpg", options);Log.i("bqt", "实际bitmap的宽高:" + bitmap.getWidth() + "-" + bitmap.getHeight() + "-" + options.inSampleSize);//1800-2700-3//注意,实际bitmap的宽高既不是我们设置的options.outWidth=1080,也不是bitmapWidth/(int) scale=1200,而是bitmapWidth/2=1800imageView.setImageBitmap(bitmap);setContentView(imageView);}}/**在保证解析出的bitmap宽高分别大于目标尺寸宽高的前提下,取可能的inSampleSize的最大值*/public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {// 原始图片的宽高int height = options.outHeight;int width = options.outWidth;int inSampleSize = 1;if (height > reqHeight || width > reqWidth) {int halfHeight = height / 2;int halfWidth = width / 2;while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {inSampleSize *= 2;}}return inSampleSize;}
简洁版demo
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ImageView imageView = new ImageView(this);Bitmap bitmap = decodeSampledBitmapFromFile(Environment.getExternalStorageDirectory().getPath() + "/a.jpg", 1080, 1920);imageView.setImageBitmap(bitmap);Log.i("bqt", "加载进内存的bitmap的宽高:" + bitmap.getWidth() + "-" + bitmap.getHeight());//1800-2700setContentView(imageView);}public static Bitmap decodeSampledBitmapFromFile(String pathName, int reqWidth, int reqHeight) {BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(pathName, options);options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);options.inJustDecodeBounds = false;return BitmapFactory.decodeFile(pathName, options);}public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {int width = options.outWidth;int height = options.outHeight;Log.i("bqt", "原始图片的宽高:" + width + "-" + height);//3600-5400int inSampleSize = 1;if (height > reqHeight || width > reqWidth) {int halfHeight = height / 2;int halfWidth = width / 2;while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {inSampleSize *= 2;}}return inSampleSize;}}
标签:
原文地址:http://www.cnblogs.com/baiqiantao/p/5494421.html