native和dalvik
dalvik
java
bitmap
native
Java
C
Bitmap
malloc
4MBitmap
13M
3M
在Android应用里,最耗费内存的就是图片资源。在Android系统中,读取位图Bitmap时,分给虚拟机中的图片的堆栈大小只有8M,如果超出了,就会出现OutOfMemory异常。
Android
OutOfMemory
及时回收Bitmap的内存
// 先判断是否已经回收if(bitmap != null && !bitmap.isRecycled()){// 回收并且置为nullbitmap.recycle();bitmap = null;}System.gc();
Bitmap bitmap = null;try {// 实例化Bitmapbitmap = BitmapFactory.decodeFile(path);} catch (OutOfMemoryError e) {//}if (bitmap == null) {// 如果实例化失败 返回默认的Bitmap对象return defaultBitmapMap;}
压缩图片如果图片像素过大可以将图片缩小,以减少载入图片过程中的内存的使用,避免异常发生。使用BitmapFactory.Options.inSampleSize就可以缩小图片。属性值inSampleSize表示缩略图大小为原始图片大小的几分之一。即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片的大小就为原始大小的1/4。如果知道图片的像素过大,就可以对其进行缩小。那么如何才知道图片过大呢?使用BitmapFactory.Options设置inJustDecodeBounds为true后,并不会真正的分配空间,即解码出来的Bitmap为null,但是可计算出原始图片的宽度和高度,即options.outWidth和options.outHeight。通过这两个值,就可以知道图片是否过大了。
BitmapFactory.Options.inSampleSize
inSampleSize
BitmapFactory.Options
inJustDecodeBounds
true
null
options.outWidth
options.outHeight
BitmapFactory.Options opts = new BitmapFactory.Options();// 设置inJustDecodeBounds为trueopts.inJustDecodeBounds = true;// 使用decodeFile方法得到图片的宽和高BitmapFactory.decodeFile(path, opts);// 打印出图片的宽和高Log.d("example", opts.outWidth + "," + opts.outHeight);
在实际项目中,可以利用上面的代码,先获取图片真实的宽度和高度,然后判断是否需要跑缩小。如果不需要缩小,设置inSampleSize的值为1。如果需要缩小,则动态计算并设置inSampleSize的值,对图片进行缩小。需要注意的是,在下次使用BitmapFactory的decodeFile()等方法实例化Bitmap对象前,别忘记将opts.inJustDecodeBound设置回false。否则获取的bitmap对象还是null。
以从Gallery获取一个图片为例讲解缩放:
public class MainActivity extends Activity {private ImageView iv;private WindowManager wm;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);wm = getWindowManager();iv = (ImageView) findViewById(R.id.iv);}// 从系统的图库里面 获取一张照片public void click(View view) {Intent intent = new Intent();intent.setAction("android.intent.action.PICK");intent.addCategory("android.intent.category.DEFAULT");intent.setType("image/*");startActivityForResult(intent, 0);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (data != null) {// 获取到系统图库返回回来图片的uriUri uri = data.getData();System.out.println(uri.toString());try {InputStream is = getContentResolver().openInputStream(uri);// 1.计算出来屏幕的宽高.int windowWidth = wm.getDefaultDisplay().getWidth();int windowHeight = wm.getDefaultDisplay().getHeight();//2. 计算图片的宽高.BitmapFactory.Options opts = new Options();// 设置 不去真正的解析位图 不把他加载到内存 只是获取这个图片的宽高信息opts.inJustDecodeBounds = true;BitmapFactory.decodeStream(is, null, opts);int bitmapHeight = opts.outHeight;int bitmapWidth = opts.outWidth;if (bitmapHeight > windowHeight || bitmapWidth > windowWidth) {int scaleX = bitmapWidth/windowWidth;int scaleY = bitmapHeight/windowHeight;if(scaleX>scaleY){//按照水平方向的比例缩放opts.inSampleSize = scaleX;}else{//按照竖直方向的比例缩放opts.inSampleSize = scaleY;}}else{//如果图片比手机屏幕小 不去缩放了.opts.inSampleSize = 1;}//让位图工厂真正的去解析图片opts.inJustDecodeBounds = false;//注意: 流的操作is = getContentResolver().openInputStream(uri);Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);iv.setImageBitmap(bitmap);} catch (Exception e) {e.printStackTrace();}}super.onActivityResult(requestCode, resultCode, data);}}