MyException - 我的异常网
当前位置:我的异常网» Android » 【转】Android-加载大分辨率图片到内存

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

www.MyException.Cn  网友分享于:2014-04-22  浏览:5次
【转】Android--加载大分辨率图片到内存

转自:http://www.cnblogs.com/plokmju/p/android_LoadBigImage.html

前言

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

  本篇博客的主要内容:

  1. 还原堆内存溢出的错误
  2. 分析堆内存溢出
  3. 如何加载大分辨率图片
  4. 示例Demo

 

还原堆内存溢出的错误

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

  代码如下:

复制代码
1 btn_loadimage.setOnClickListener(new View.OnClickListener() {
2             
3             @Override
4             public void 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中需要用到的属性:

  • inJustDecodeBounds:如果设置为true,将不把图片的像素数组加载到内存中,仅加载一些额外的数据到Options中。
  • outHeight:图片的高度。
  • outWidth:图片的宽度。
  • inSampleSize:如果设置,图片将依据此采样率进行加载,不能设置为小于1的数。例如设置为4,分辨率宽和高将为原来的1/4,这个时候整体所占内存将是原来的1/16。

  

示例Demo

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

复制代码
 1 package cn.bgxt.loadbigimg;
 2 
 3 import 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;
14 
15 public class MainActivity extends Activity {
16     private Button btn_loadimage;
17     private ImageView iv_bigimage;
18 
19     @Override
20     protected void 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
30             public void onClick(View v) {
31                 // Bitmap bitmap=BitmapFactory.decodeFile("/sdcard/a.jpg");
32                 // iv_bigimage.setImageBitmap(bitmap);
33 
34                 BitmapFactory.Options opts = new Options();
35                 // 不读取像素数组到内存中,仅读取图片的信息
36                 opts.inJustDecodeBounds = true;
37                 BitmapFactory.decodeFile("/sdcard/a.jpg", opts);
38                 // 从Options中获取图片的分辨率
39                 int imageHeight = opts.outHeight;
40                 int imageWidth = opts.outWidth;
41 
42                 // 获取Android屏幕的服务
43                 WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
44                 // 获取屏幕的分辨率,getHeight()、getWidth已经被废弃掉了
45                 // 应该使用getSize(),但是这里为了向下兼容所以依然使用它们
46                 int windowHeight = wm.getDefaultDisplay().getHeight();
47                 int windowWidth = wm.getDefaultDisplay().getWidth();
48 
49                 // 计算采样率
50                 int scaleX = imageWidth / windowWidth;
51                 int scaleY = imageHeight / windowHeight;
52                 int scale = 1;
53                 // 采样率依照最大的方向为准
54                 if (scaleX > scaleY && scaleY >= 1) {
55                     scale = scaleX;
56                 }
57                 if (scaleX < scaleY && scaleX >= 1) {
58                     scale = scaleY;
59                 }
60 
61                 // false表示读取图片像素数组到内存中,依照设定的采样率
62                 opts.inJustDecodeBounds = false;
63                 // 采样率
64                 opts.inSampleSize = scale;
65                 Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/a.jpg", opts);
66                 iv_bigimage.setImageBitmap(bitmap);
67 
68             }
69         });
70     }
71 }
复制代码

  效果展示:

 

  源码下载

 

总结

  本篇博客到这里就讲解了如何加载一个大分辨率的图片到内存中并使用它。不过一般好一点的图片处理软件,都会有图片放大功能,如果仅做此处理,单纯的把处理后的图片放大,会影响显示效果,图片还原度不高。一般会重新获取放大区域的图片的分辨率像素数组,然后重新处理加载到内存中进行显示。

文章评论

漫画:程序员的工作
漫画:程序员的工作
程序员都该阅读的书
程序员都该阅读的书
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员应该关注的一些事儿
程序员应该关注的一些事儿
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
旅行,写作,编程
旅行,写作,编程
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
Java程序员必看电影
Java程序员必看电影
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
那些争议最大的编程观点
那些争议最大的编程观点
程序员和编码员之间的区别
程序员和编码员之间的区别
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
一个程序员的时间管理
一个程序员的时间管理
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
老程序员的下场
老程序员的下场
中美印日四国程序员比较
中美印日四国程序员比较
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
总结2014中国互联网十大段子
总结2014中国互联网十大段子
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
如何成为一名黑客
如何成为一名黑客
鲜为人知的编程真相
鲜为人知的编程真相
我的丈夫是个程序员
我的丈夫是个程序员
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
 程序员的样子
程序员的样子
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
程序员的鄙视链
程序员的鄙视链
我是如何打败拖延症的
我是如何打败拖延症的
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
10个调试和排错的小建议
10个调试和排错的小建议
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有