Android--加载大分辨率图片到内存
前言
在使用ImageView显示图片的时候,直接加载一个图片资源到内存中,经常会出现内存溢出的错误,这是因为有些图片的分辨率比较高,把它直接加载到内存中之后,会导致堆内存溢出的问题。这篇博客就来讲解一下Android的堆内存以及如何在Android应用中加载一个高分辨率的图片。关于ImageView不熟悉的朋友,可以看看之前的博客:Android--ImageView。
本篇博客的主要内容:
还原堆内存溢出的错误
首先来还原一下堆内存溢出的错误。首先在SD卡上放一张照片,分辨率为(3776 X 2520),大小为3.88MB,是我自己用相机拍的一张照片。应用的布局很简单,一个Button一个ImageView,然后按照常规的方式,使用BitmapFactory加载一张照片并使用一个ImageView展示。
代码如下:
btn_loadimage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bitmap=BitmapFactory.decodeFile("/sdcard/a.jpg");
iv_bigimage.setImageBitmap(bitmap);
}
}
当点击按钮后,程序会报错,查看日志为:
先来分析一下这个错误,首先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来演示上面提到的内容,代码中注释比较清晰,这里就不再累述了。
package cn.bgxt.loadbigimg; import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.view.Menu;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView; public class MainActivity extends Activity {
private Button btn_loadimage;
private ImageView iv_bigimage; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); btn_loadimage = (Button) findViewById(R.id.btn_loadimage);
iv_bigimage = (ImageView) findViewById(R.id.iv_bigimage); btn_loadimage.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// Bitmap bitmap=BitmapFactory.decodeFile("/sdcard/a.jpg");
// iv_bigimage.setImageBitmap(bitmap); BitmapFactory.Options opts = new Options();
// 不读取像素数组到内存中,仅读取图片的信息
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile("/sdcard/a.jpg", opts);
// 从Options中获取图片的分辨率
int imageHeight = opts.outHeight;
int imageWidth = opts.outWidth; // 获取Android屏幕的服务
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
// 获取屏幕的分辨率,getHeight()、getWidth已经被废弃掉了
// 应该使用getSize(),但是这里为了向下兼容所以依然使用它们
int windowHeight = wm.getDefaultDisplay().getHeight();
int windowWidth = wm.getDefaultDisplay().getWidth(); // 计算采样率
int scaleX = imageWidth / windowWidth;
int scaleY = imageHeight / windowHeight;
int scale = 1;
// 采样率依照最大的方向为准
if (scaleX > scaleY && scaleY >= 1) {
scale = scaleX;
}
if (scaleX < scaleY && scaleX >= 1) {
scale = scaleY;
} // false表示读取图片像素数组到内存中,依照设定的采样率
opts.inJustDecodeBounds = false;
// 采样率
opts.inSampleSize = scale;
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/a.jpg", opts);
iv_bigimage.setImageBitmap(bitmap); }
});
}
}
效果展示:
总结
本篇博客到这里就讲解了如何加载一个大分辨率的图片到内存中并使用它。不过一般好一点的图片处理软件,都会有图片放大功能,如果仅做此处理,单纯的把处理后的图片放大,会影响显示效果,图片还原度不高。一般会重新获取放大区域的图片的分辨率像素数组,然后重新处理加载到内存中进行显示。

Android--加载大分辨率图片到内存的更多相关文章
- 转 Android--加载大分辨率图片到内存
在使用ImageView显示图片的时候,直接加载一个图片资源到内存中,经常会出现内存溢出的错误,这是因为有些图片的分辨率比较高,把它直接加载 到内存中之后,会导致堆内存溢出的问题.这篇博客就来讲解一下 ...
- 图片_ _Android--加载大分辨率图片到内存
http://www.cnblogs.com/plokmju/p/android_LoadBigImage.html#3084005 前言 在使用ImageView显示图片的时候,直接加载一个图片资源 ...
- Android开发之多媒体编程之加载大分辨率图片
Android中图片占用内存的大小=图片的总像数*每个像数占用的大小. Android保存图片像素信息使用ARGB,意思是每个像素占用4个字节. 以分辨率为2400*3200的图片来说,加载到Andr ...
- Android学习笔记_51_转android 加载大图片防止内存溢出
首先来还原一下堆内存溢出的错误.首先在SD卡上放一张照片,分辨率为(3776 X 2520),大小为3.88MB,是我自己用相机拍的一张照片.应用的布局很简单,一个Button一个ImageView, ...
- Android -- 加载大图片的方法
在android中要加载一张大图片到内存中如果通过如下方式进行: Bitmap bitmap= BitmapFactory.decodeFile("/sdcard/a.jpg"); ...
- Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)【系列1】
Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)[系列1] Android在加载或者处理超大巨型图片 ...
- Android -- 加载大图片到内存,从gallery获取图片,获取图片exif信息
1. 加载大图片到内存,从gallery获取图片 android默认的最大堆栈只有16M, 图片像素太高会导致内存不足的异常, 需要将图片等比例缩小到适合手机屏幕分辨率, 再加载. 从gallery ...
- Android 加载大图片到内存
本文演示android中图片加载到内存 首先设计界面: 代码如下: <LinearLayout xmlns:android="http://schemas.android.com/ap ...
- Android加载大图片OOM异常解决
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过 ...
随机推荐
- Hive基础测试操作
一.Hive测试 1.查看数据库 show databases; 2.使用某个数据库,如默认数据库 user default; 3.创建表 create table if not exist itst ...
- easynetq发布订阅demo实现注意事项
最近开始在项目中使用easynetq,大概了解了下api,在网上看了下示例,结果没有一个运行成功的,一个最简单的发布订阅都没有成功.我是直接运行起来别人的示例,不应该啊,后来一直分析一直分析,最后发现 ...
- 手机QQ公众号亿级消息实时群发架构
编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由孙子荀分享.转载请注明来自高可用架构公众号 ArchNotes. 孙子荀,2009 年在华为从事内核和分布式系统的开发工作:2011 ...
- HCNA(华为)_DHCP篇
在大型的企业网络中,会有大量的主机或设备需要获取IP地址等网络参数.如果采用手工配置,工作量大 且不好管理,如果有用户擅自修改网络参数,还有可能会造成IP地址冲突等问题.使用动态主机配置协DHCP 来 ...
- ODM、JDM、OEM概念
OEM (Original Equipment Manufacturer) - 原始设备制造商 委托生产,或者说“代工生产”,其含义是品牌厂商不直接制造产品,而是负责设计和开发新产品,控制销售“渠道” ...
- npm几个常用命令
npm install xxx 安装模块(例如npm install express 就会默认安装express的最新版本,也可以通过在后面加版本号的方式安装指定版本,如npm install exp ...
- summary of week
Summary of week Catalog 计算机基础 解释器 编码 数据类型 输入 输出 变量 注释 运算符 条件判断 循环 Content 计算机基础 计算机组成 软件 解释器 操作系统 : ...
- js array 对象
Javascript 对象: Array 对象:数组 创建方法: 1, var a = new Array() 2,var a = new Array(3) 3,var a = new Array(“ ...
- 201771010126 王燕《面向对象程序设计(java)》第十一周学习总结
实验十一 集合 实验时间 2018-11-8 1.实验目的与要求 (1) 掌握Vetor.Stack.Hashtable三个类的用途及常用API: Vector类实现了长度可变的数组. Vecto ...
- DOM-节点概念-属性
1.节点的概念 页面中的所有内容,包括标签,属性,文本(文字,空格,回车,换行等),也就是说页面的所有内容都可以叫做节点. 2.节点相关的属性 2.1.节点分类 **标签节点:**比如 div 标签, ...