分享好友 移动开发首页 频道列表

Android开发艺术探索学习笔记(三)—Android性能优化之Bitmap导致的内存溢出

Android开发  2016-12-06 13:080

原本计划是按照章节顺序学习《Android开发艺术探索》这本书的,Android性能优化这部分也是本书的最后一章。但是周末的时候,友盟线下反馈的公司项目的一个错误让我不得不提前学习这一块的知识。先看看线下反馈的错误吧:

Android开发艺术探索学习笔记(三)—Android性能优化之Bitmap导致的内存溢出

java.lang.OutOfMemoryError:应用程序内存溢出,俗称OOM,是指应用程序在申请内存时,没有足够的内存空间供其使用而出现的问题。Android中常见的导致内存溢出的场景有以下几种:

1.静态变量导致的内存溢出 2.单例模式导致的内存溢出 3.大量位图的加载导致的内存溢出

这里我们根据反馈上来崩溃日志,可以很清楚的看到我们这次的内存溢出是由第三种原因导致的。那么就从这个问题入手,找到解决Android中因Bitmap导致内存溢出的办法。

一.检测内存溢出

新版的Android Studio给我们提供了内存分析的可视化界面,但是精确的检测并找到内存泄漏的原因,我们还需要第三方的工具。

推荐简书上这篇文章,说的很详细,这里就不重复了。对照这篇文章一步一步进行即可:

使用新版Android Studio检测内存泄露和性能

二.分析具体原因项目中应用其实很常见,在客户端选择图片以后,上传到七牛,然后再把七牛返回的url存储到服务器上。可问题就出现在图片上传到七牛的这一步,我们先看一下刚开始的代码怎么写的:

public static byte[] getSmallBitmap(String filePath) {
        Bitmap bitmap;
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        //获取缩放比例
        options.inSampleSize = calculateInSampleSize(options, 1000, 1000);
        options.inJustDecodeBounds = false;
        bitmap = BitmapFactory.decodeFile(filePath, options);
        //bitmap转bytes
        byte[] bytes = Bitmap2Bytes(bitmap);
        if (null != bitmap && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
        return bytes;
    }

代码其实就是将Bitmap转化为byte数组,然后将这个byte数组传到七牛上,之前图片处理一直是这样做的,也没有出现任何问题。为什么这次却报了OOM呢,中场暂停一下,先来了解一下图片相关的知识:

以我红米2的测试机为例,一张图库里的图片分辨率是1080*1920px,它的文件大小为192KB,此时这张图片是以文件的形式存在于硬盘上。那么我们如果以Bitmap的形式将这张图片加载到应用程序中,占用的内存是多少呢:

1080*1920*4=8294400B=7.9M

图片(BitMap)占用的内存=图片长度 * 图片宽度*单位像素占用的字节数

前两个分别代表长度与宽度(像素单位),单位像素占用字节数其大小由BitmapFactory.Options的inPreferredConfig变量决定。

inPreferredConfig为Bitmap.Config类型,是个枚举类型,对应如下:

Android开发艺术探索学习笔记(三)—Android性能优化之Bitmap导致的内存溢出

默认值为ARGB_8888。一张质量并不高的图片以Bitmap的形式加载到内存中占用的内存就快8M,当一次性加载大量位图的时候,肯定会远远超过应用程序所分配的内存空间,从而导致OOM。对于配置不高,系统版本过低,应用程序内存紧张的手机来说,出现这种情况的概率会大大增加。所以我们有必要对图片进行必要的压缩,减小内存,避免OOM。

三.优化过程:

图片的压缩分为两种:质量压缩与尺寸压缩,区别是质量压缩并不会改变图片的尺寸,而尺寸压缩则会改变图片的尺寸。那么它们分别应用在哪些地方呢,还是以刚才那张图片为例子。

当我把以文件形式存在在硬盘上的图片,以Bitmap的形式加载到内存中的时候,我就必须进行尺寸压缩,因为质量压缩并不会改变Bitmap所占内存的大小,而尺寸压缩由于是减小了图片的像素,所以它直接对bitmap产生了影响,从而使所占内存减小。代码具体实现过程:

public static byte[] getSmallBitmap(String filePath) {
        Bitmap bitmap;
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inSampleSize = calculateInSampleSize(options, 1000, 1000);
        options.inJustDecodeBounds = false;

        try {
            bitmap = BitmapFactory.decodeFile(filePath, options);
        } catch (Exception e) {
            options.inSampleSize = calculateInSampleSize(options, 500, 500);
            options.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeFile(filePath, options);
        }
        byte[] bytes = Bitmap2Bytes(bitmap != null ? bitmap : null);
        if (null != bitmap && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
        return bytes;
    }


   private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio > widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }

代码分析:

Bitmap的实例化是通过BitmapFactory提供的接口生成的,利用BitmapFactory可以从一个指定文件中,利用decodeFile()解出Bitmap,也可以定义的图片资源中,利用decodeResource()解出Bitmap。它的主要方法及配置选项如下:

Android开发艺术探索学习笔记(三)—Android性能优化之Bitmap导致的内存溢出

实例化一个BitmapFactory.Options,并配置它的相关属性:

options.inJustDecodeBounds = true,表示解析图片的时候,只解析长度和宽度,不载入图片,这样就节省内存开支。

options.inPreferredConfig = Bitmap.Config.RGB_565,前文提到的表格一目了然,这样会节省一半的内存。

options.inSampleSize = calculateInSampleSize(options, 1000, 1000),计算缩放的比例,inSampleSize只能是2的整数次幂,如果不是的话,向下取得最大的2的整数次幂,比如比例为7,向下寻找2的整数次幂,就是4。如果缩放比例是4的话,7.9M的那张图片最后占用的内存会是7.9/16=0.49M,完全不用担心OOM的发生。

options.inJustDecodeBounds = false,计算好压缩比例后,去加载解析原图。

bitmap = BitmapFactory.decodeFile(filePath, options),解析文件得到Bitmap。

与之前的代码相比,优化之后我改变了options.inPreferredConfig的值,并且在转换得到Bitmap的时候加上了try/catch,出现异常的话进一步扩大缩放比例,减小内存,防止OOM。

以上是尺寸压缩的相关办法,那么质量压缩又用在哪里呢:

private static byte[] Bitmap2Bytes(Bitmap bm) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.JPEG, 75, baos);
        return baos.toByteArray();
    }

当图片从Bitmap的形式转化为二进制的形式(文件形式)时, 我们可以适当使用质量压缩,加快传递速率。还是之前那张图片,192KB的图片经过以上方法的质量压缩以后,大小为144KB左右(虽然图片大小变小,但是转换为Bitmap的时候,这张经过质量压缩后的图片所占内存还是不会变的,仍然为7.9M)。随着手机硬件配置的提升,手机图片的质量也越来越高,所以质量压缩还是很必要的。

以上就是从这次Bitmap导致的内存溢出学习总结到的一些知识,希望能对你有所帮助。下一篇再见~~~

查看更多关于【Android开发】的文章

展开全文
相关推荐
反对 0
举报 0
评论 0
图文资讯
热门推荐
优选好物
更多热点专题
更多推荐文章
AsyncTask 工作原理(上)
AsyncTask 是一种轻量级是异步任务类,它可以在线程池中执行后台任何,将执行的进度和最终结果传递给主线程,并在主线程中更新UI。AsyncTask 是一个抽象类,其构造函数//Params:传入doInBackground 中的参数类型//Progress: 后台执行进度的类型,传入onProgre

0评论2016-11-10163

vysor 原理以及 Android 同屏方案
vysor是一个免root实现电脑控制手机的chrome插件,目前也有几款类似的通过电脑控制手机的软件,不过都需要root权限,并且流畅度并不高。vysor没有多余的功能,流畅度也很高,刚接触到这款插件时我惊讶于它的流畅度以及免root,就一直对它的实现原理很感兴趣。

0评论2016-10-13222

Picasso 解析 (1)- 一张图片是如何加载出来的
前言Picasso是JakeWharton大神在github上的一个开源图片加载框架,使用起来极其方便,甚至只需要一行代码就可以搞定图片加载:Picasso.with(context).load(http://i.imgur.com/DvpvklR.png).into(imageView);具体如何使用该框架我就不在这里赘述了,大家可以

0评论2016-10-04251

Android 图片缓存之初识 Glide(三)
前言:前面总结学习了图片的使用以及Lru算法,今天来学习一下比较优秀的图片缓存开源框架。技术本身就要不断的更迭,从最初的自己使用SoftReference实现自己的图片缓存,到后来做电商项目自己的实现方案不能满足项目的需求改用Afinal,由于Afinal不再维护而选

0评论2016-08-3091

Android编程实现擦除Bitmap中某一块的方法
这篇文章主要介绍了Android编程实现擦除Bitmap中某一块的方法,涉及Android操作Bitmap颜色像素值调整的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

0评论2015-11-2588

android中Bitmap用法(显示,保存,缩放,旋转)实例分析
这篇文章主要介绍了android中Bitmap用法,以实例形式较为详细的分析了android中Bitmap操作图片的显示、保存、缩放、旋转等相关技巧,需要的朋友可以参考下

0评论2015-11-08152

Android生成带圆角的Bitmap图片
这篇文章主要介绍了Android生成带圆角的Bitmap图片,涉及Android通过Canvas实现绘制带圆角的图片相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

0评论2015-09-13127

Afianl框架里面的FinalBitmap加载网络图片
这篇文章主要介绍了Afianl框架里面的FinalBitmap加载网络图片的相关资料,需要的朋友可以参考下

0评论2015-09-11124

Android Bitmap详细介绍
Android中Bitmap的常见操作整理一览,需要的朋友可以参考下

0评论2015-09-07112

Android界面 NotificationManager使用Bitmap做图标
Android界面 NotificationManager使用Bitmap做图标,如何实现呢,本文将介绍解决方法,需要的朋友可以参考下

0评论2015-09-05120

基于android示例程序(bitmapfun) 高效加载图片让人无语地方
尝试了使用git上的一个开源项目afinal(bitmapfun的封装版)来加载图片,但是在测试的时候发现了一个问题,新的图片加载器(bitmapfun)比之前用的ImageDownloader要慢很多,特别是在网络状况不好的时候,那简直是太让人无语了

0评论2015-08-2475

更多推荐