热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android性能优化系列之bitmap图片优化

在日常开发的APP,大部分时候需要想用户展示图片信息,图片最终对应Android中的Bitmap对象。而对于APP端来说Bitmap又是一个比较麻烦的问题,主要表现在Bitmap是非常占用内存的对象,处理不当将导致APP运行卡顿甚至出现OOM

背景

Android开发中,加载图片过多、过大很容易引起OutOfMemoryError异常,即我们常见的内存溢出。因为Android对单个应用施加内存限制,默认分配的内存只有几M(具体视不同系统而定)。而载入的图片如果是JPG之类的压缩格式(JPG支持最高级别的压缩,不过该压缩是有损的),在内存中展开会占用大量的内存空间,也就容易形成内存溢出。那么高效的加载Bitmap是很重要的事情。Bitmap在Android中指的是一张图片,图片的格式有.jpg .jpg .webp 等常见的格式。

如何选择图片格式

一个原则: 在保证图片视觉不失真前提下,尽可能的缩小体积

Android目前常用的图片格式有jpg,jpeg和webp

  • png:无损压缩图片格式,支持Alpha通道,Android切图素材多采用此格式
  • jpeg:有损压缩图片格式,不支持背景透明,适用于照片等色彩丰富的大图压缩,不适合logo
  • webp:是一种同时提供了有损压缩和无损压缩的图片格式,派生自视频编码格式VP8,从谷歌官网来看,无损webp平均比jpg小26%,有损的webp平均比jpeg小25%~34%,无损webp支持Alpha通道,有损webp在一定的条件下同样支持,有损webp在Android4.0(API 14)之后支持,无损和透明在Android4.3(API18)之后支持

采用webp能够在保持图片清晰度的情况下,可以有效减小图片所占有的磁盘空间大小

图片压缩

图片压缩可以从三个方面去考虑:

1.质量

质量压缩并不会改变图片在内存中的大小,仅仅会减小图片所占用的磁盘空间的大小,因为质量压缩不会改变图片的分辨率,而图片在内存中的大小是根据widthheight一个像素的所占用的字节数计算的,宽高没变,在内存中占用的大小自然不会变,质量压缩的原理是通过改变图片的位深和透明度来减小图片占用的磁盘空间大小,所以不适合作为缩略图,可以用于想保持图片质量的同时减小图片所占用的磁盘空间大小。另外,由于jpg是无损压缩,所以设置quality无效,

  /**
 * 质量压缩
 *
 * @param format  图片格式 jpeg,png,webp
 * @param quality 图片的质量,0-100,数值越小质量越差
 */
public static void compress(Bitmap.CompressFormat format, int quality) {
    File sdFile = Environment.getExternalStorageDirectory();
    File originFile = new File(sdFile, "originImg.jpg");
    Bitmap originBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath());
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    originBitmap.compress(format, quality, bos);
    try {
        FileOutputStream fos = new FileOutputStream(new File(sdFile, "resultImg.jpg"));
        fos.write(bos.toByteArray());
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.采样率

采样率压缩是通过设置BitmapFactory.Options.inSampleSize,来减小图片的分辨率,进而减小图片所占用的磁盘空间和内存大小。

设置的inSampleSize会导致压缩的图片的宽高都为1/inSampleSize,整体大小变为原始图片的inSampleSize平方分之一,当然,这些有些注意点:

  • inSampleSize小于等于1会按照1处理
  • inSampleSize只能设置为2的平方,不是2的平方则最终会减小到最近的2的平方数,如设置7会按4进行压缩,设置15会按8进行压缩。
/**
 * 
 * @param inSampleSize  可以根据需求计算出合理的inSampleSize
 */
public static void compress(int inSampleSize) {
    File sdFile = Environment.getExternalStorageDirectory();
    File originFile = new File(sdFile, "originImg.jpg");
    BitmapFactory.Options optiOns= new BitmapFactory.Options();
    //设置此参数是仅仅读取图片的宽高到options中,不会将整张图片读到内存中,防止oom
    options.inJustDecodeBounds = true;
    Bitmap emptyBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath(), options);

    options.inJustDecodeBounds = false;
    options.inSampleSize = inSampleSize;
    Bitmap resultBitmap = BitmapFactory.decodeFile(originFile.getAbsolutePath(), options);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
    try {
        FileOutputStream fos = new FileOutputStream(new File(sdFile, "resultImg.jpg"));
        fos.write(bos.toByteArray());
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

3.缩放

通过减少图片的像素来降低图片的磁盘空间大小和内存大小,可以用于缓存缩略图

 /**
     *  缩放bitmap
     * @param context
     * @param id
     * @param maxW
     * @param maxH
     * @return
     */
    public static Bitmap resizeBitmap(Context context,int id,int maxW,int maxH,boolean hasAlpha,Bitmap reusable){
        Resources resources = context.getResources();
        BitmapFactory.Options optiOns= new BitmapFactory.Options();
        // 只解码出 outxxx参数 比如 宽、高
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources,id,options);
        //根据宽、高进行缩放
        int w = options.outWidth;
        int h = options.outHeight;
        //设置缩放系数
        options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH);
        if (!hasAlpha){
            options.inPreferredCOnfig= Bitmap.Config.RGB_565;
        }
        options.inJustDecodeBounds = false;
        //设置成能复用
        options.inMutable=true;
        options.inBitmap=reusable;
        return BitmapFactory.decodeResource(resources,id,options);
    }

    /**
     * 计算缩放系数
     * @param w
     * @param h
     * @param maxW
     * @param maxH
     * @return 缩放的系数
     */
    private static int calcuteInSampleSize(int w,int h,int maxW,int maxH) {
        int inSampleSize = 1;
        if (w > maxW && h > maxH){
            inSampleSize = 2;
            //循环 使宽、高小于 最大的宽、高
            while (w /inSampleSize > maxW && h / inSampleSize > maxH){
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }
}
  • 使用JPEG库,在jni层使用哈夫曼算法去压缩图片

Android的图片引擎使用的是阉割版的skia引擎,去掉了图片压缩中的哈夫曼算法

void write_JPEG_file(uint8_t *data, int w, int h, jint q, const char *path) {
//    3.1、创建jpeg压缩对象
    jpeg_compress_struct jcs;
    //错误回调
    jpeg_error_mgr error;
    jcs.err = jpeg_std_error(&error);
    //创建压缩对象
    jpeg_create_compress(&jcs);
//    3.2、指定存储文件  write binary
    FILE *f = fopen(path, "wb");
    jpeg_stdio_dest(&jcs, f);
//    3.3、设置压缩参数
    jcs.image_width = w;
    jcs.image_height = h;
    //bgr
    jcs.input_compOnents= 3;
    jcs.in_color_space = JCS_RGB;
    jpeg_set_defaults(&jcs);
    //开启哈夫曼功能
    jcs.optimize_coding = true;
    jpeg_set_quality(&jcs, q, 1);
//    3.4、开始压缩
    jpeg_start_compress(&jcs, 1);
//    3.5、循环写入每一行数据
    int row_stride = w * 3;//一行的字节数
    JSAMPROW row[1];
    while (jcs.next_scanline 

因为涉及到jni部分,暂时只贴一下使用的代码,后面会写一些jni部分的博客与大家分享。

  • 设置图片可以复用

图片复用主要就是指的复用内存块,不需要在重新给这个bitmap申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。

需要注意的是inBitmap只能在3.0以后使用。2.3上,bitmap的数据是存储在native的内存区域,并不是在Dalvik的内存堆上。

使用inBitmap,在4.4之前,只能重用相同大小的bitmap的内存区域,而4.4之后你可以重用任何bitmap的内存区域,只要这块内存比将要分配内存的bitmap大就可以。这里最好的方法就是使用LRUCache来缓存bitmap,后面来了新的bitmap,可以从cache中按照api版本找到最适合重用的bitmap,来重用它的内存区域。

   BitmapFactory.Options optiOns= new BitmapFactory.Options();
        // 只解码出 outxxx参数 比如 宽、高
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources,id,options);
        //根据宽、高进行缩放
        int w = options.outWidth;
        int h = options.outHeight;
        //设置缩放系数
        options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH);
        if (!hasAlpha){
            options.inPreferredCOnfig= Bitmap.Config.RGB_565;
        }
        options.inJustDecodeBounds = false;
        //设置成能复用
        options.inMutable=true;
        options.inBitmap=reusable;
  • 使用图片缓存

android中有一个LruCache是基于最记最少使用算法实现的一个线程安全的数据缓存类,当超出设定的缓存容量时,优先淘汰最近最少使用的数据LruCache的LRU缓存策略是利用LinkedHashMap来实现的,并通过封装get/put等相关方法来实现控制缓存大小以及淘汰元素,但不支持为null的key和value。 我们可以使用JakeWharton提供的一个开源库github.com/JakeWharton… 来实现我们图片缓存的逻辑

省略了内存和磁盘的部分。

到此这篇关于Android 性能优化系列之bitmap图片优化的文章就介绍到这了,更多相关Android 性能优化内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
  • Android中Bitmap与Drawable的区别有哪些?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更 ... [详细]
  • Iamtryingtodevelopanapponcanvas,Iamdrawingabitmaponthecanvas.Afterdrawing,iamtryin ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 突破MIUI14限制,自定义胶囊图标、大图标样式,支持任意APP
    本文介绍了如何突破MIUI14的限制,实现自定义胶囊图标和大图标样式,并支持任意APP。需要一定的动手能力和主题设计师账号权限或者会主题pojie。详细步骤包括应用包名获取、素材制作和封包获取等。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 网上有很多解决android加载bitmap内存溢出的方法,搜了一圈做下整理总结。项目里需求是拍摄多图之后上传,部分手机会内存溢出。常用一种解决方法:即将载入的图片缩小,这种方式以牺牲图片的质量为代价 ... [详细]
  • 在Android中进行图像处理的任务时,有时我们希望将处理后的结果以图像文件的格式保存在内部存储空间中,本文以此为目的,介绍将Bitmap对象的数据以P ... [详细]
  • BitmapbitmapBitmapFactory.decodeResource(r,R.drawable.icon).copy(Bitmap.Config.ARGB_8888,tr ... [详细]
  • Android开发之网络图片查看方法BitmapFactory.decodeStream()学习01
    Android实现网络图片的查看有好几种方法,但这本身是一种很耗时的操作,可以通过直接获取和操作线程的方法,自己学习使用了BitmapFactory.decodeStream()在代码中自己写了注释, ... [详细]
author-avatar
雇Anitamia
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有