前言

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

  本篇博客的主要内容:

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

还原堆内存溢出的错误

  首先来还原一下堆内存溢出的错误。首先在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--加载大分辨率图片到内存的更多相关文章

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

    在使用ImageView显示图片的时候,直接加载一个图片资源到内存中,经常会出现内存溢出的错误,这是因为有些图片的分辨率比较高,把它直接加载 到内存中之后,会导致堆内存溢出的问题.这篇博客就来讲解一下 ...

  2. 图片_ _Android--加载大分辨率图片到内存

    http://www.cnblogs.com/plokmju/p/android_LoadBigImage.html#3084005 前言 在使用ImageView显示图片的时候,直接加载一个图片资源 ...

  3. Android开发之多媒体编程之加载大分辨率图片

    Android中图片占用内存的大小=图片的总像数*每个像数占用的大小. Android保存图片像素信息使用ARGB,意思是每个像素占用4个字节. 以分辨率为2400*3200的图片来说,加载到Andr ...

  4. Android学习笔记_51_转android 加载大图片防止内存溢出

    首先来还原一下堆内存溢出的错误.首先在SD卡上放一张照片,分辨率为(3776 X 2520),大小为3.88MB,是我自己用相机拍的一张照片.应用的布局很简单,一个Button一个ImageView, ...

  5. Android -- 加载大图片的方法

    在android中要加载一张大图片到内存中如果通过如下方式进行: Bitmap bitmap= BitmapFactory.decodeFile("/sdcard/a.jpg"); ...

  6. Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)【系列1】

    Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)[系列1] Android在加载或者处理超大巨型图片 ...

  7. Android -- 加载大图片到内存,从gallery获取图片,获取图片exif信息

    1. 加载大图片到内存,从gallery获取图片 android默认的最大堆栈只有16M, 图片像素太高会导致内存不足的异常, 需要将图片等比例缩小到适合手机屏幕分辨率, 再加载. 从gallery ...

  8. Android 加载大图片到内存

    本文演示android中图片加载到内存 首先设计界面: 代码如下: <LinearLayout xmlns:android="http://schemas.android.com/ap ...

  9. Android加载大图片OOM异常解决

      尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过 ...

随机推荐

  1. docker-compose.yml 配置文件详解及项目发布

    摘自:https://blog.csdn.net/qq_36148847/article/details/79427878 docker部署tomcat项目 1.上传war包2.制作镜像 Docker ...

  2. 动态DP之全局平衡二叉树

    目录 前置知识 全局平衡二叉树 大致介绍 建图过程 修改过程 询问过程 时间复杂度的证明 板题 前置知识 在学习如何使用全局平衡二叉树之前,你首先要知道如何使用树链剖分解决动态DP问题.这里仅做一个简 ...

  3. JSPatch 热更新

    JSPatch 是一个 iOS 动态更新框架,只需在项目中引入极小的引擎,就可以使用 JavaScript 调用任何 Objective-C/Swift 原生接口. 获得脚本语言的优势,为项目动态添加 ...

  4. 开始食用grpc(之一)

    开始食用grpc(之一) 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9501353.html ```   记一次和一锅们压马路,路过一咖啡厅(某巴克),随口 ...

  5. 把ssl模块加入到已经编译好的apache中实现HTTPS

    为了使Apache支持https访问,系统需要安有apache.openssl.mod_ssl.so 1.安装openssl: 基本上系统都已经安装了,在/usr/bin/openssl下,直接使用o ...

  6. (转)CentOS7中防火墙的基本操作

    目录 1.firewalld简介 2.安装firewalld 3.运行.停止.禁用firewalld 4.配置firewalld 5 打开端口 学习apache安装的时候需要打开80端口,由于cent ...

  7. 数据分析——numpy

    DIKW DATA-->INFOMATION-->KNOWLEDGE-->WISDOM 数据-->信息-->知识-->智慧 爬虫-->数据库-->数据分 ...

  8. CDI Features

    概述 如果说EJB,JPA是之前JEE(JEE5及JEE5之前)中里程碑式的规范,那么在JEE6,JEE7中CDI可以与之媲美,CDI(Contexts and Dependency Injectio ...

  9. The SQL Server instance returned an invalid or unsupported protocol version during login negotiatio

    在使用.net core 连接sqlserver的时候遇到了这个问题 从字面意思理解大致是个什么版本不支持, 谷歌一下吧,ok,看到这个2000我就知道什么问题了 我的数据库还是2000的,总算把20 ...

  10. Bandwagon的配置记录(二) —— ftp文件传输

    SSH登录服务器 登录的方法在Bandwagon的配置记录(一) —— kexue上网 配置前的准备 1.新建一个目录(  /home/ftp  ),以后可以把文件放在这里,这里相当于是个中转站 cd ...