我们在平常使用Bitmap的过程中经常会遇到OOM异常,为此困扰了我两三天时间,接下来把我自己的解决方法讲述一下:

首先来看看为什么使用Bitmap会导致OOM异常的,假如采用Bitmap来加载一个596KB(1920*1152)大小的图片,那么实际上在加载到内存中的时候占用空间的大小将不只是596KB那么大,具体多大呢?计算方法是:

图片的长度 * 图片的宽度 * 单位像素占用的字节数

对于单位像素点占用的字节数,官方支持的图片解码方式有四种:

ALPHA_8:只存储透明度信息,没有颜色值;

ARGB_4444:在API 13之后用ARGB_8888来代替;

ARGB_8888:把每个像素点的透明度、R、G、B值都采用一个byte来表示,即需要4个字节来存储像素点的颜色信息;

RGB_565:仅仅需要两个字节来存储像素点的颜色信息,他不会存储像素点的透明度,而且采用5比特来存放R颜色值,采用6比特来存放G颜色值,采用5比特来存放B颜色值;

就以上面596KB的图片如果采用ARGB_8888来进行图片解码的话,实际占用内存的大小是:1920*1152*4 B = 8640KB,为了印证这个的结论,先来个小Demo看看具体怎么获得图片变成Bitmap占用内存的大小:

布局文件很简单,就只有ImageView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><ImageView android:id="@+id/imageView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/meimei"/>
</LinearLayout>

接下来是BitmapActivity

public class BitmapActivity extends Activity {public ImageView imageView = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.bitmap_activity);imageView = (ImageView) findViewById(R.id.imageView);Drawable drawable = imageView.getDrawable();if(drawable instanceof BitmapDrawable){BitmapDrawable bitmapDrawable = (BitmapDrawable)drawable;Bitmap bitmap = bitmapDrawable.getBitmap();int rowBytes = bitmap.getRowBytes();int height = bitmap.getHeight();long memorySize = rowBytes*height;System.out.println("width:  "+bitmap.getWidth());System.out.println("height:  "+height);System.out.println("size:   "+(float)memorySize/1024+"KB");}}
}

这里的getRowBytes用于返回Bitmap中每一行像素点占用的字节数,getHeight获得高度,相当于行数,二者相乘正好就是图片解码之后将要占用的内存空间大小;

查看Logcat输出:

看到没有呢?相对于596KB来说,足足是他的14.5倍,这还只是像素不是太大的图片,如果像素更大呢?一张图片就占用这么大内存,如果是很多张图片呢?不OOM才怪呢

那么为什么我们平常的应用程序在加载很多图片的时候也没见过有什么异常啊,他们是怎么实现的呢?这里讲解一种最直观,最简单的实现,那就是压缩图片,因为在一个很小的布局中显示高分辨率的图片对于你界面来说除了占用很大内存外是没有任何意义的,完全可以显示其压缩后的小图呀,那么我们该怎么做呢?别急,下面就是了:

定义图片加载布局bitmap_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><ImageView android:id="@+id/imageView1"android:layout_width="50dp"android:layout_height="50dp"/>
</LinearLayout>

很简单,只有一个ImageView

定义BitmapActivity

public class BitmapActivity extends Activity {public ImageView imageView = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.bitmap_activity);imageView = (ImageView) findViewById(R.id.imageView1);ImageAsyncTask task = new ImageAsyncTask(imageView,50,50);task.execute("http://image.baidu.com/search/down?tn=download&word=download&ie=utf8&fr=detail&url=http%3A%2F%2Fimg1.pconline.com.cn%2Fpiclib%2F200904%2F28%2Fbatch%2F1%2F32910%2F12408824046039mk21hbi75.jpg&thumburl=http%3A%2F%2Fimg2.imgtn.bdimg.com%2Fit%2Fu%3D3414739956%2C4196877666%26fm%3D21%26gp%3D0.jpg");}/*** 计算出压缩比* @param options* @param reqWith* @param reqHeight* @return*/public int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){//通过参数options来获取真实图片的宽、高int width = options.outWidth;int height = options.outHeight;int inSampleSize = 1;//初始值是没有压缩的if(width > reqWidth || height > reqHeight){//计算出原始宽与现有宽,原始高与现有高的比率int widthRatio = Math.round((float)width/(float)reqWidth);int heightRatio = Math.round((float)height/(float)reqHeight);//选出两个比率中的较小值,这样的话能够保证图片显示完全 inSampleSize = widthRatio < heightRatio ? widthRatio:heightRatio;}System.out.println("压缩比:  "+inSampleSize);return inSampleSize;}/*** 将InputStream转换为Byte数组* @param in* @return*/public static byte[] inputStreamToByteArray(InputStream in){ByteArrayOutputStream outputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;try {while((len = in.read(buffer)) != -1){outputStream.write(buffer, 0, len);}} catch (IOException e) {e.printStackTrace();}finally{try {in.close();outputStream.close();} catch (IOException e) {e.printStackTrace();}}return outputStream.toByteArray();}class ImageAsyncTask extends AsyncTask<String, Void, Bitmap>{public ImageView iv;public int reqWidth;public int reqHeight;public ImageAsyncTask(ImageView imageView,int reqWidth,int reqHeight){this.iv = imageView;this.reqWidth = reqWidth;this.reqHeight = reqHeight;}@Overrideprotected Bitmap doInBackground(String... params) {URL url;HttpURLConnection connection = null;InputStream in = null;Bitmap beforeBitmap = null;Bitmap afterBitmap = null;try {url = new URL(params[0]);connection = (HttpURLConnection) url.openConnection();in = connection.getInputStream();BitmapFactory.Options options = new BitmapFactory.Options();//设置BitmapFactory.Options的inJustDecodeBounds属性为true表示禁止为bitmap分配内存options.inJustDecodeBounds = true;byte[] data = inputStreamToByteArray(in);beforeBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);//这次调用的目的是获取到原始图片的宽、高,但是这次操作是没有写内存操作的options.inSampleSize = calculateInSampleSize(options,reqWidth, reqHeight); //设置这次加载图片需要加载到内存中options.inJustDecodeBounds = false;afterBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);float afterSize = (float)(afterBitmap.getRowBytes()*afterBitmap.getHeight());System.out.println("压缩之后的图片大小:  "+(float)afterSize/1024+"KB");} catch (Exception e) {System.out.println(e.toString());}return afterBitmap;}@Overrideprotected void onPostExecute(Bitmap result) {if(result != null)imageView.setImageBitmap(result);}}
}

不要忘记添加访问网络的权限:

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

其中第8行会开启开启线程来加载图片同时会将ImageView通过构造函数传递给线程,第9行execute的参数就是我们要加载图片的地址,调用之后会执行ImageAsyncTask的doInBackground方法,在这个方法中会通过HttpURLConnection来获得对应地址图片的输入流,接着第85行获得Bitmap的Options对象,这个对象的inJustDecodeBounds属性用来设置在解码图片的时候是否将其加入到内存中,因为一般Bitmap图片都是按照像素点来存储的,这样的话会很占用内存,将inJustDecodeBounds设置为true的话表示在解码图片的时候不会将其加入到内存中,第88行我们调用inputStreamToByteArray将in输入流转换成了byte数组,原因在于将流进行固化,因为后面我们可能会多次使用流中的数据,但是输入流只能使用一次,用过之后是不能回滚的,第89行通过BitmapFactory的decodeByteArray方法对图片数据byte数组进行了解码,但是不会加入内存,第四个参数options就是我们之前创建的Options对象,这个方法执行结束后就会获得图片的头部信息,他会存储到options中,如果你仔细点的话会发现beforeBitmap的值是等于null的,就是因为他设置inJustDecodeBounds为true没有使用内存的原因啦!

随后我们调用calculateInSampleSize计算出压缩比,计算方法也很简单,就是通过options的outWidth和outHeight获得图片的实际宽度和高度,分别与所要求图片的宽度和高度相除,取两者结果的较小值作为压缩比;

第90行设置了options的压缩比,91行设置inJustDecodeBounds为false表示解码图片的时候需要将其加入到内存中,当然这里占用内存的大小将远远实际图片需要占用的大小,达到了真正意义上对图片进行压缩减少使用内存的操作;

最后在onPostExecute中将doInBackground返回的bitmap设置为ImageView需要显示的图片,这里需要注意的是设置ImageView的图片操作必须是在onPostExecute中,因为onPostExecute是属于主线程的,而doInBackground是属于子线程的,只有主线程可以更新UI操作的,具体分析可以参见:android-----AsyncTask源码分析

源码下载:

android-----解决Bitmap内存溢出的一种方法(图片压缩技术)相关推荐

  1. 缩放图片,解决bitmap 内存溢出out of memory的问题

    缩放图片,解决bitmap 内存溢出out of memory的问题 参考文章: (1)缩放图片,解决bitmap 内存溢出out of memory的问题 (2)https://www.cnblog ...

  2. Fatal Error: Out of memory php内存溢出处理三种方法

    有时候我们在运行php程序的时候会发现 Fatal Error: Out of memory 这样的提示,这有可能是程序中用到了大量了变量和对象,导致分配的内存不够用. 修改php.ini文件里的me ...

  3. Java中OutOfMemoryError(内存溢出)的三种情况及解决办法

    Java中OutOfMemoryError(内存溢出)的三种情况及解决办法 相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各 ...

  4. java 内存 溢出_java内存溢出的几种原因和解决办法是什么?

    java内存溢出的几种原因和解决办法是什么? java内存溢出的几种原因和解决办法是: 第一类内存溢出,也是大家认为最多,第一反应认为是的内存溢出,就是堆栈溢出: 那什么样的情况就是堆栈溢出呢?当你看 ...

  5. JVM内存溢出的几种方式与解决方法

    内存溢出 JVM运行时首先需要类加载器(classLoader)加载所需类的字节码文件.加载完毕交由执行引擎执行,在执行过程中需要一段空间来存储数据(类比CPU与主存).这段内存空间的分配和释放过程正 ...

  6. 关于 android oom(内存溢出的分析)

    laozhu1124 android oom 全解析 Android oom 有时出现很频繁,这一般不是Android设计的问题,一般是我们的问题. 就我的经验而言,出现oom,无非主要是以下几个方面 ...

  7. linux/centos 解决Tomcat内存溢出,centostomcat

    2019独角兽企业重金招聘Python工程师标准>>> linux/centos 解决Tomcat内存溢出,centostomcat Tomcat本身不能直接在计算机上运行,需要依赖 ...

  8. android 避内存溢出,Android避免内存溢出(Out of Memory)方法总结

    Android避免内存溢出(Out of Memory)方法总 结 避免内存溢出的方法,主要是对以下三个方面对程序进行优化武汉Android培训 内存引用 在处理内存引用之前,我们先来复习下什么是强引 ...

  9. java内存溢出原因及解决_java内存溢出的原因和解决方法

    java内存溢出的原因和解决方法 发布时间:2020-06-15 17:57:39 来源:亿速云 阅读:85 作者:元一 内存溢出含义: 内存溢出(out of memory)通俗理解就是内存不够,通 ...

最新文章

  1. 宁波大学计算机专业复试,2016年宁波大学信息科学与工程学院计算机专业考研复试题库. (1)...
  2. 字节开启员工期权兑换,126美元每股;
  3. javascript弹出div(一)
  4. AKS使用Azure File实现动态持久化存储
  5. 项目开发一些注意事项
  6. 五分钟教你如何用函数计算部署钉钉群发机器人
  7. 微软在 Build 2020 上“展示”新版 Edge for Linux
  8. java运行出现XML_eclipse开发环境下,项目运行时出现pom.xml报错(java.io.PrintWriter)...
  9. 尼日利亚年轻人推动该国登上比特币谷歌搜索排名榜首
  10. SilverLight 初探一
  11. 常用api查询网站记录
  12. -Xlint:deprecation
  13. 【等价转换】—— min/max 的转换与互相转换
  14. linux使用163的yum源配置
  15. 旗袍时尚:青花瓷与青花时装
  16. JAVA面试常考系列七
  17. 2010年度CSDN十大博客文章(个人收藏)
  18. iOS 15提示“此App的开发者需要更新APP以在此IOS版本上正常工作”
  19. 阿里云建站产品有哪些?如何选择?
  20. Git学习笔记及一些问题(廖雪峰版)

热门文章

  1. C++ 怎样输入字符串
  2. es6新增数组、数组去重、es6新特性
  3. 国产降噪耳机哪款降噪效果好?降噪效果好的降噪耳机推荐
  4. vue项目内封装axios.,使用Mock,搭建前后端分离环境。Axios + Promise + Mock
  5. Cocos2d-x添加IOS手机震动
  6. 她一个月挣多少钱,给家里多少钱
  7. solidworks2019无法连接到服务器(-15...)或(-8...)
  8. 计算机应用视觉传媒是什么,视觉传播
  9. moe安装指南_macOS 安装简明教程
  10. Spring Boot 整合微信小程序实现登录与增删改查