0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Android端:优化Bitmap内存的几种方法

张康康 2019-07-29 18:27 次阅读

作者 | Video++极链科技移动端Team秦鹏程

整理 | 包包

初识

Bitmap是图像处理的最重要类之一。用它可以获取图像文件信息,进行图像颜色变换、剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

许多 Android 开发者都对 Bitmap 不陌生,其作为显示图片的载体,会经常接触。而在日常开发中对图片的处理通常会用到第三方的开源库:Glide、Fresco、Picasso...,这些已经足够完善的工具不需要让我们考虑处理 Bitmap 的细节,这使得我们对其不是那么熟悉。

Bitmap 实实在在是内存使用的“大客户”。如何更好的使用 Bitmap,减少其对App内存的使用,是 Android 优化方面不可回避的问题,因此,本文从常规的 Bitmap 使用,到 Bitmap 内存计算,最后分析如何更有效的使用 Bitmap。

了解

Bitmap 占用了多大的内存

Bitmap 用来处理位图,每一张图片的每个像素点都会被读取,每个像素点的大小决定了 Bitmap 的内存大小。

所以计算内存大小的公式为:

占用的内存大小 = 像素总数量(宽x高)x 每个像素的字节大小

单个像素的字节大小

单个像素的字节大小由Bitmap的一个可配置的参数Config来决定。Bitmap中,存在一个枚举类Config,定义了Android中支持的Bitmap配置:

dd10223e3e0a41f3ba4e5857adf086e1


Android系统中,默认Bitmap加载图片,使用ARGB_8888模式。

Bitmap 占用内存大小实例

我们准备一张分辨率为 1920x1080,大小为 273KB 的 jpg 图片,放在手机SD 卡中,调用 BitmatFactory.decodeFile() 加载并显示到一个大小为 640x320 的 ImageView 中,占用的内存如下:


从计算内存大小的公式可以得到加载这张图片使用了大约 7m 的内存,即使是手机内存普遍上涨的今天,这样的开销也是法接受的。

在刚才的实例中,我们是将图片放在了手机的外置 SD 卡中,现在,我们将图片分别放到项目工程的 mipmap-xhdpi, mipmap-xxhdpi, mipmap-xxxhdpi 这三个资源目录中,调用 BitmatFactory.decodeResource() 加载到同样的 ImageView 中看看加载的情况:


我们发现,图片放在不同的资源目录中、使用不同的方法加载,占用的内存也会不同,为了探究这其中原理,需要通过观察 Bitmap.decode 的源码,这一过程是有 native 来完成的,所以我们找到 BitmapFactory.cpp#nativeDecode 开始跟踪,省略了其他不相关的代码:

1b627a3bc6b94c8da34fdf0b6a806f5d


上述代码中,最终 bitmap 是通过 canvas 绘制出来,而 canvas 绘制前有 scale 的操作 scale = (float) targetDensity / density; 这一行代码决定,即缩放的倍率和 targetDensity 和 density 相关,而这两个参数都是从传入的 options 中获取到的,再到 Bitmap.Options 中找到相关的参数:

• inDensity:Bitmap 位图自身的密度、分辨率

• inTargetDensity: Bitmap 最终绘制的目标位置的分辨率

其中 inDensity 和图片存放的资源文件的目录有关,同一张图片放置在不同目录下会有不同的值:

a4a77501595a4a0ca17ea7fad53a2faa


通过以上两个实例,我们得出了 decodeResource() 和 decodeFile() 的区别:

•decodeResource 用于读取Res、Raw等资源,得到的是图片的原始尺寸 * 缩放系数(inDensity)

•decodeFile 用于读取SD卡上的图,得到的是图片的原始尺寸

手动设置缩放系数

在 Bitmap.Options 中还有一个为 inScaled 的属性,如果设置为 false,则不进行缩放,如果设置为 true 或者不设置,则根据 inDensity 和 inTargetDensity 计算缩放系数。 如果你不想依赖于这个系统本身的 density,你可以手动设置 inDensity 和 inTargetDensity 来控制缩放系数:

168ca8c69c4b432e92b3f6eac127cc07


压缩方式 inSampleSize & quality

inSampleSize 指的是压缩分辨率,取值必须为 2 的幂(当不为2的幂时,解码器会取与该值最接近的2的幂),例如,当 inSampleSize = 2 时,一张 1920x1080 的图片,将会被缩小为 960x540,相应的它的像素数和内存占用都被缩小为原来的 1/4。

quality 正如字面意思指的是图片品质,在代码中对应的 api 为:

5c6276611390424d8bb176604c582155


CompressFormat 为 Bitmap 中的枚举类,有三个可用值:

• JPEG:表示以 JPEG 压缩算法进行图像压缩,压缩后的格式可以是 “.jpg” 或者 “.jpeg” ,是一种有损压缩。

• PNG:表示以 PNG 压缩算法进行图像压缩,压缩后的格式可以是 “.png” ,是一种无损压缩。

• WEBP:表示以 WebP 压缩算法进行图像压缩,压缩后的格式可以是 “.webp” ,是一种有损压缩,质量相同的情况下,WebP 格式图像的体积要比 JPEG 格式图像小40%。美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”。

quality 为图片的品质,取值为 0-100,100 代表最高品质,不被压缩。另外,类似 PNG 这种无损格式会忽略 quality 的设置 stream 为图片被压缩后被保存在的输出流。

然而 Bitmap.compress 方法确实可以压缩图片,但压缩的是存储大小,即放到 disk 上的大小。

调试

现在我们通过几个实例,来验证一下以上的结论,首先来看一下两种压缩方式占用内存的影响:

inSampleSize

45aa8fc9b5214936bcaf103256312559


显示结果 :


以上 ImageView 的大小(640x320),用来加载 1920x1080 的图片确实有些浪费,所以经过计算,将原图压缩后发现图片占用内存的大小减少到原图的 1/10,如果原图本身与控件的大小相差不多,这时候还要缩放的话就会影响到图片显示的质量。

降低图片品质

0b74cd8226bd4e15819acd14b12b53dd


显示结果 :


使用降低图片质量的方式压缩图片,可以发现尽管已经降低了 90% 的品质,图片也变得模糊,但其占用的内存与直接加载还是一样的。

改变 Bitmap.Config

我们已经知道, Bitmap 加载图片默认使用的 config 为 ARGB_8888,而且 ALPHA_8 是只有透明度的, 所以我们来看看改为 ARGB_4444 和 RGB_565 所显示的结果,只需要在 decode 的时候传入设置好的 options 参数,所以这里直接给出显示结果:


可以看到将 config 改为 ARGB_4444,所占用的内存与原图一样,而 RGB_565,变得是原图的 1/2,所以结论也不言而喻了,另外 ARGB_4444,已被官方标记为废弃。

总结

在上面,我们将一张 1920x1080 的图片,不做任何处理解析到内存中,将近占用的 7M,想象一下这样的开销发生在一个图片列表中,内存占用将达到非常夸张的地步。从之前Bitmap占用内存的计算公式来看,减少内存主要可以通过以下几种方式:

• 使用低色彩的解析模式,如RGB565,减少单个像素的字节大小。这样大约能减少一半的内存开销。Android 默认是使用 ARGB_8888 配置来处理色彩,占用4字节,改用RGB_565,将只占用2字节,代价是显示的色彩将相对少,适用于对色彩丰富程度要求不高的场景。

• 资源文件合理放置,高分辨率图片可以放到高分辨率目录下。和图片的具体分辨率有关,建议开发中,高分辨率的图像应该放置到合理的资源目录下,注意到Android默认放置的资源目录是对应于160dpi,目前手机屏幕分辨率越来越高,此处能节省下来的开销也是很可观的。理论上,图片放置的资源目录分辨率越高,其占用内存会越小,但是低分辨率图片会因此被拉伸,显示上出现失真。另一方面,高分辨率图片也意味着其占用的本地储存也变大。

• 图片缩小,减少尺寸。理论上根据适用的环境,是可以减少十几倍的内存使用的,它基于这样一个事实:源图片尺寸一般都大于目标需要显示的尺寸,因此可以通过缩放的方式,来减少显示时的图片宽高,从而大大减少占用的内存。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Android
    +关注

    关注

    12

    文章

    3851

    浏览量

    125637
  • BITMAP
    +关注

    关注

    0

    文章

    4

    浏览量

    6333
收藏 人收藏

    评论

    相关推荐

    改变异步电动机的转速有几种方法

    改变异步电动机的转速有几种方法  改变异步电动机的转速可以通过以下几种方法实现:调节输入电压、改变动态电阻、更换极数、调整定子电阻、调整转子电阻和改变电源频率等。下面将对这些方法进行详细介绍。 1.
    的头像 发表于 02-20 11:43 466次阅读

    PWM产生的几种方法总结

    PWM产生的方法有很多种,小编将常用的几种产生方法作了一个整理以及分类,下面我们来了解一下。
    的头像 发表于 01-11 09:15 902次阅读
    PWM产生的<b class='flag-5'>几种方法</b>总结

    电子元器件测阻抗有几种方法?网络分析仪阻抗不匹配怎么调?

    电子元器件测阻抗的方法主要有以下几种: 直接测量法:将电子元器件直接连接到测量仪器上,通过测量仪器直接得到阻抗值。这种方法适用于测量精度要求不高的场合,如电路调试过程中。 交流阻抗法:在电子元器件
    的头像 发表于 12-12 16:05 386次阅读
    电子元器件测阻抗有<b class='flag-5'>几种方法</b>?网络分析仪阻抗不匹配怎么调?

    javajvm调优有几种方法

    基本概念。JVM(Java Virtual Machine,Java虚拟机)是Java运行时环境的核心组件,负责解释和执行Java字节码文件。JVM调优的目标是优化JVM的内存使用、垃圾回收、线程管理等方面,以提高应用程序的性能和可用性。 下面是
    的头像 发表于 12-05 11:11 987次阅读

    开关电源输出纹波抑制的几种方法

    电子发烧友网站提供《开关电源输出纹波抑制的几种方法.doc》资料免费下载
    发表于 11-15 09:11 0次下载
    开关电源输出纹波抑制的<b class='flag-5'>几种方法</b>

    查看Linux系统内存使用情况的几种方法

    在Linux系统中,内存监控是优化系统性能的关键。本文为你介绍12种方法,帮助你全面掌握Linux系统的内存使用情况。这些方法包括查看/pr
    的头像 发表于 11-13 09:30 1735次阅读
    查看Linux系统<b class='flag-5'>内存</b>使用情况的<b class='flag-5'>几种方法</b>

    单片机IO口扩展有哪几种方法

    单片机IO口扩展有哪几种方法
    发表于 11-01 07:47

    提高三极管的开关速度的几种方法

    提高三极管的开关速度的几种方法 三极管是一种常用的电子器件,广泛应用于电子电路中。在电子电路中,三极管通常起到开关和放大的作用。其中,三极管的开关速度是电子电路设计中一个非常重要的指标。在实际
    的头像 发表于 10-31 14:43 1225次阅读

    单片机软件滤波的几种方法

    电子发烧友网站提供《单片机软件滤波的几种方法.pdf》资料免费下载
    发表于 10-20 15:09 2次下载
    单片机软件滤波的<b class='flag-5'>几种方法</b>

    Docker镜像国内加速的几种方法

    在国内,拉取 Docker 镜像速度慢/时不时断线/无账号导致限流等,比较痛苦. 这里提供加速/优化几种方法
    的头像 发表于 10-19 10:02 926次阅读
    Docker镜像国内加速的<b class='flag-5'>几种方法</b>

    如何降低设备功耗,降低采集设备功耗的几种方法

    如何降低设备功耗,降低采集设备功耗的几种方法 工程监测传感器 以下是降低数采设备功耗的一些方法优化硬件设计:通过选择低功耗的芯片、使用更高效的转换器、减少功率损耗等方式来优化硬件设
    的头像 发表于 10-11 09:29 605次阅读

    适用于Android操作系统的MTE用户指南

    解决内存错误的原理。 ·在Android项目中启用MTE:介绍在Android项目中启用MTE的几种不同的方法。.MTE错误报告:如何获取和
    发表于 08-02 08:56

    Linux内存相关知识科普

    ,Linux 内核几种内存管理的方法内存使用场景以及内存使用的那些坑。**从内存的原理和结构,
    发表于 07-25 14:43 551次阅读
    Linux<b class='flag-5'>内存</b>相关知识科普

    兼顾内存和速度的C语言代码优化方法

    在本篇文章中,我(指原作者)收集了很多经验和方法。应用这些经验和方法,可以帮助我们从执行速度和内存使用等方面来优化C语言代码。
    发表于 07-24 15:44 279次阅读
    兼顾<b class='flag-5'>内存</b>和速度的C语言代码<b class='flag-5'>优化</b>的<b class='flag-5'>方法</b>

    在数字电路中TTL与非门的多余的输入应如何处理?

    在数字电路中TTL与非门的多余的输入应如何处理?有几种方法
    发表于 04-28 10:49