鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 手机系统开发 > android > >

加载大分辨率图片到内存

来源:互联网 作者:佚名 时间:2013-11-29 10:34
Android--加载大分辨率图片到内存 前言 在使用ImageView显示图片的时候,直接加载一个图片资源到内存中,经常会出现内存溢出的错误,这是因为有些图片的分辨率比较高,把它直接加载到内存中之后,会导致堆内存溢出的问题。这篇博客就来讲解一下Android的堆内

Android--加载大分辨率图片到内存

前言

  在使用ImageView显示图片的时候,直接加载一个图片资源到内存中,经常会出现内存溢出的错误,这是因为有些图片的分辨率比较高,把它直接加载到内存中之后,会导致堆内存溢出的问题。这篇博客就来讲解一下Android的堆内存以及如何在Android应用中加载一个高分辨率的图片。关于ImageView不熟悉的朋友,可以看看之前的博客:Android--ImageView。

  本篇博客的主要内容:

 

还原堆内存溢出的错误

  首先来还原一下堆内存溢出的错误。首先在SD卡上放一张照片,分辨率为(3776 X 2520),大小为3.88MB,是我自己用相机拍的一张照片。应用的布局很简单,一个Button一个ImageView,然后按照常规的方式,使用BitmapFactory加载一张照片并使用一个ImageView展示。

  代码如下:

1 btn_loadimage.setOnClickListener(new View.OnClickListener() { 2 3 @Override onClick(View v) { 5 Bitmap bitmap=BitmapFactory.decodeFile("/sdcard/a.jpg"); 6 iv_bigimage.setImageBitmap(bitmap); 7 } 8 }

  当点击按钮后,程序会报错,查看日志为:

  先来分析一下这个错误,首先dalvikvm(Android虚拟机)发现需要的内存38MB大于应用的堆内存24MB,这个时候尝试使用软加载的方式加载数据,我们知道当内存不足的时候dalvikvm会自动进行GC(Garbage Collection),大概清理了55k的空间出来,耗时203毫秒,但是内存还是不够,所以最后发生堆内存溢出的错误。

 

分析堆内存溢出

  Android系统主要用于低能耗的移动设备,所以对内存的管理有很多限制,一个应用程序,Android系统缺省会为其分配最大16MB(某些机型是24MB)的空间作为堆内存空间,我这里使用的模拟器调试的,这个模拟器被设定为24MB,可以在Android Virtual Device Manager中查看到。

  而这里的图片明明只有3.88MB,远远小于Android为应用分配的堆内存,而加载到内存中,为什么需要消耗大约38MB的内存呢?

  我们都知道,图片是由一个一个点分布组成的(分辨率),通常加载这类数据都会在内存中创建一个二维数组,数组中的每一项代表一个点,而这个图片的分辨率是3776 * 2520,每一点又是由ARGB色组成,每个色素占4个Byte,所以这张图片加载到内存中需要消耗的内存为:

  3776 * 2520 * 4byte = 38062080byte

  大约需要38MB的内存才能正确加载这张图片,这就是上面错误描述需要38MB的内存空间,大小略有出入,因为图片还有一些Exif信息需要存储,会比仅靠分辨率计算要大一些。

 

如何加载大分辨率图片

  有时候我们确实会需要加载一些大分辨率的图片,但是对于移动设备而言,哪怕加载能成功那么大的内存也是一种浪费(屏幕分辨率限制),所以就需要想办法把图片按照一定比率压缩,使分辨率降低,以至于又不需要耗费很大的堆内存空间,又可以最大的利用设备屏幕的分辨率来显示图片。这里就用到一个BitmapFactory.Options对象,,下面来介绍它。

  BitmapFactory.Options为BitmapFactory的一个内部类,它主要用于设定与存储BitmapFactory加载图片的一些信息。下面是Options中需要用到的属性:

  

示例Demo

  下面通过一个简单的Demo来演示上面提到的内容,代码中注释比较清晰,这里就不再累述了。

1 package cn.bgxt.loadbigimg; android.os.Bundle; 4 import android.os.Environment; 5 import android.app.Activity; 6 import android.graphics.Bitmap; 7 import android.graphics.BitmapFactory; 8 import android.graphics.BitmapFactory.Options; 9 import android.view.Menu; 10 import android.view.View; 11 import android.view.WindowManager; 12 import android.widget.Button; 13 import android.widget.ImageView; MainActivity extends Activity { 16 private Button btn_loadimage; 17 private ImageView iv_bigimage; 18 19 @Override onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_main); 23 24 btn_loadimage = (Button) findViewById(R.id.btn_loadimage); 25 iv_bigimage = (ImageView) findViewById(R.id.iv_bigimage); 26 27 btn_loadimage.setOnClickListener(new View.OnClickListener() { 28 29 @Override onClick(View v) { 31 // Bitmap bitmap=BitmapFactory.decodeFile("/sdcard/a.jpg"); BitmapFactory.Options opts = new Options(); opts.inJustDecodeBounds = true; 37 BitmapFactory.decodeFile("/sdcard/a.jpg", opts); imageHeight = opts.outHeight; 40 int imageWidth = opts.outWidth; WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); 44 // 获取屏幕的分辨率,getHeight()、getWidth已经被废弃掉了 windowHeight = wm.getDefaultDisplay().getHeight(); 47 int windowWidth = wm.getDefaultDisplay().getWidth(); scaleX = imageWidth / windowWidth; 51 int scaleY = imageHeight / windowHeight; 52 int scale = 1; (scaleX > scaleY && scaleY >= 1) { 55 scale = scaleX; 56 } 57 if (scaleX < scaleY && scaleX >= 1) { 58 scale = scaleY; 59 } opts.inJustDecodeBounds = false; opts.inSampleSize = scale; 65 Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/a.jpg", opts); 66 iv_bigimage.setImageBitmap(bitmap); 67 68 } 69 }); 70 } 71 }

  效果展示:

网友评论
<