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

    文章

    3912

    浏览量

    127011
  • BITMAP
    +关注

    关注

    0

    文章

    4

    浏览量

    6361
收藏 人收藏

    评论

    相关推荐

    环路测试方法有哪几种

    。环路测试的目的是确保循环能够正确地开始、执行和终止,以及在循环内部的逻辑是否正确。 环路测试通常包括以下几种方法: 基本路径测试 :这是最基础的环路测试方法,它关注于测试循环的基本执行路径。测试者会创建测试用例,确保循环能够按照预期执行,包括循
    的头像 发表于 09-12 14:35 356次阅读

    直流无刷电机调速有几种方法及应用

    多种多样,每种方法都有其特定的应用场景和优缺点。 1. 电压控制调速 电压控制调速是通过改变电机供电电压的大小来实现调速的方法。这种方法简单易行,但效率较低,因为电压的变化会导致电机的磁通量变化,从而影响电机的性能。 1.
    的头像 发表于 09-03 10:43 855次阅读

    如何检测内存泄漏

    检测内存泄漏是软件开发过程中一项至关重要的任务,它有助于识别和解决那些导致程序占用过多内存资源,从而影响程序性能甚至导致程序崩溃的问题。以下将详细阐述几种常见的内存泄漏检测
    的头像 发表于 07-30 11:50 1242次阅读

    产生脉冲信号有几种方法

    脉冲信号是一种在特定时间间隔内具有特定幅度的信号,它在电子学、通信、控制等领域有着广泛的应用。产生脉冲信号的方法有很多种,下面将介绍几种常见的方法。 555定时器产生脉冲信号 555定时器是一种
    的头像 发表于 07-15 10:35 846次阅读
    产生脉冲信号有<b class='flag-5'>几种方法</b>

    mesh的内存占用能否优化

    余110kb可用。 请问,mesh的内存占用问题能否优化?为何系统剩余大概60K0内存以下的时候系统会因内存不足重启?
    发表于 06-28 15:32

    接地电阻的测量有哪几种方法

    接地电阻的测量对于确保电气系统的安全性和可靠性至关重要。存在几种不同的方法来测量接地电阻,每种方法都有其特定的应用场景和技术要求。
    的头像 发表于 05-07 14:17 1.3w次阅读

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

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

    电阻应变片的温度补偿方法几种

    电阻应变片的温度补偿方法几种? 电阻应变片的温度补偿方法有以下几种: 1. 温度传感器补偿方法 温度传感器补偿
    的头像 发表于 02-04 18:14 4914次阅读

    PWM产生的几种方法总结

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

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

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

    javajvm调优有几种方法

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

    多线程同步的几种方法

    多线程同步是指在多个线程并发执行的情况下,为了保证线程执行的正确性和一致性,需要采用特定的方法来协调线程之间的执行顺序和共享资源的访问。下面将介绍几种常见的多线程同步方法。 互斥锁(Mutex
    的头像 发表于 11-17 14:16 1098次阅读

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

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

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

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

    机器人阻抗控制有几种方法

    的动态响应关系,通过改变阻抗,可以调节机器人与外界的动态作用。 一般来说,机器人阻抗控制主要分为两种方法:基于位置的阻抗控制和基于力的阻抗控制。 1.基于位置的阻抗控制:让机器人电机在位置模式下工作,通过发送目标位
    的头像 发表于 11-08 18:08 1353次阅读
    机器人阻抗控制有<b class='flag-5'>几种方法</b>