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

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

原本计划是按照章节顺序学习《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导致的内存溢出学习总结到的一些知识,希望能对你有所帮助。下一篇再见~~~

 
标签: Bitmap 安卓开发
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 如何进行网络视频截图/获取视频的缩略图
    如何进行网络视频截图/获取视频的缩略图
    小编导读:获取视频的缩略图,截图正在播放的视频某一帧,是在音视频开发中,常遇到的问题。本文是主要用于点播中截图视频,同时还可以获取点播视频的缩略图进行显示,留下一个问题,如下图所示, 如果要获取直播中节目视频缩略图,该怎么做呢?(ps:直播是直
  • 不得不知道的图片加载框架之Glide
    不得不知道的图片加载框架之Glide
    简介在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app。特点(1)使用简单(2)可配置度高,自适应程度高(3)支持常见图片
  • 一个实用方便的图片控件SImageView
    一个实用方便的图片控件SImageView
    控件介绍 这是一个简单到 sImageView.setImageUrls(http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg); 设置一个网址即可显示图片的控件相对 ImageView 功能的扩展的控件, 但是没有继承 ImageView 直接继承的 View . 比如 QQ群组头像 ,
  • Android图形图像使用总结
    Android图形图像使用总结
    一.图形特效(一)特效的实现方式在Android中,提供了3种方式实现特效,setXXX方法,postXXX和preXXX()方法。1.setXXX方法用于直接设置Matrix的值,每使用一次setXXX()方法,整个的Matrix都会变掉。2.postXXX方法用于采用后乘的方式为Matrix设置值,可以连续多次
  • AsyncTask 工作原理(上)
    AsyncTask 是一种轻量级是异步任务类,它可以在线程池中执行后台任何,将执行的进度和最终结果传递给主线程,并在主线程中更新UI。AsyncTask 是一个抽象类,其构造函数//Params:传入doInBackground 中的参数类型//Progress: 后台执行进度的类型,传入onProgre
  • vysor 原理以及 Android 同屏方案
    vysor是一个免root实现电脑控制手机的chrome插件,目前也有几款类似的通过电脑控制手机的软件,不过都需要root权限,并且流畅度并不高。vysor没有多余的功能,流畅度也很高,刚接触到这款插件时我惊讶于它的流畅度以及免root,就一直对它的实现原理很感兴趣。
  • 简单实现 Android 图片三级缓存机制
    简单实现 Android 图片三级缓存机制
    用户在使用我们的APP时,通常会重复浏览一些图片,这时如果每一次浏览都需要通过网络获取图片,那么将会非常流量。为了节省用户流量,提高图片加载效率,我们通常使用图片三级缓存策略,即通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪
  • Picasso 解析 (1)- 一张图片是如何加载出来的
    前言Picasso是JakeWharton大神在github上的一个开源图片加载框架,使用起来极其方便,甚至只需要一行代码就可以搞定图片加载:Picasso.with(context).load(http://i.imgur.com/DvpvklR.png).into(imageView);具体如何使用该框架我就不在这里赘述了,大家可以
  • 自定义圆形图片、可控位置圆角图片
    自定义圆形图片、可控位置圆角图片
    一.原理1.下面的Xfermode子类可以改变这种行为:AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。PorterDuffXfermode 这是一个非常强大的转换模式,
  • Android的oom详解
    Android的oom详解
    oom的定义OOM(out of memory)即内存泄露。一个程序中,已经不需要使用某个对象,但是因为仍然有引用指向它垃圾回收器就无法回收它,当该对象占用的内存无法被回收时,就容易造成内存泄露。Android的oom原因1.资源对象没关闭造成的内存泄露,try catch final
    09-12 BitmapOOM
点击排行