性能优化:Android中Bitmap内存大小优化的几种常见方式

简介: 性能优化:Android中Bitmap内存大小优化的几种常见方式

性能优化:Android中Bitmap内存大小优化的几种常见方式

Android中的bitmap是比较占用内存的,bitmap的大小直接影响到了应用占用内存的大小。bitmap占用内存大小的计算方式为:

bitmap内存大小 = bitmap宽度(px) * bitmap长度(px) * 一个像素点占用的字节数
AI 代码解读

BitmapFactory给我们提供了多个decode方法,我们可以从不同的数据源中加载bitmap,如下图所示:

BitmapFactory加载Bitmap

一个像素点占用的字节数对应的参数是Bitmap.Config,它是一个枚举类,具体取值如下:

Bitmap.Config

而每个新建的Bitmap的默认Config的值就Bitmap.Config.ARGB_8888,它表示一个像素点占用4个字节((8 + 8 + 8 + 8) / 8 = 4byte)。

改变Bitmap内存大小

根据上面的原理,我们可以从两个方面减少Bitmap的内存占用,一个是改变Bitmap的宽高,另一个是改变Bitmap.Config的值,将Bitmap.Config.ARGB_8888改为占用字节更少的Bitmap.Config.ARGB_4444或者Bitmap.Config.RGB_565

具体有以下几种方法:

1、采样率压缩(改变Bitmap大小)。

2、通过martix进行压缩(改变Bitmap大小)。Bitmap.createBitmap或者Bitmap.createScaledBitmap方法。

3、更改Bitmap.Config格式。

具体示例如下:
我们先来看下原图大小:

原图

原图大小为640px * 360px,且是放置在assets目录下的,表示系统不会有缩放操作;如果是放在对应的drawable目录下,则会由系统进行相应的缩放操作。

1、采样率压缩(改变Bitmap大小)。

val assetFileDescriptor = assets.openFd("maomi.jpg")
val fileInputStream = assetFileDescriptor.createInputStream()
val bmpInSampleSizeJpg = BitmapOptionsUtil.decodeSampledBitmapFromFileStream(
    fileInputStream,
    ScreenUtils.dip2px(this, 80f),
    ScreenUtils.dip2px(this, 45f)
)
val descBmpConfigJpg =
    "height:${bmpInSampleSizeJpg?.height},\nwidth:${bmpInSampleSizeJpg?.width},\nallocationByteCount:${bmpInSampleSizeJpg?.allocationByteCount}byte,\n" +
            "byteCount:${bmpInSampleSizeJpg?.byteCount}byte,\nrowBytes:${bmpInSampleSizeJpg?.rowBytes}byte,\ndensity:${bmpInSampleSizeJpg?.density}"
tvInSampleSizeJpgInfo.text = "Jpg通过inSampleSize压缩后的信息:$descBmpConfigJpg"
ivInSampleSizeJpg.setImageBitmap(bmpInSampleSizeJpg)
AI 代码解读

采样率

可以明显的看到,图片的宽高各自缩小为原来的1/4,bitmap占用内存大小为原来的1/16。

采样率压缩Bitmap大小的方式,适用于与原有的图片对象宽高比较大,而目标Bitmap的尺寸比较小,此时就没有必要将Bitmap按照原有的大尺寸加载进来,可以有效的避免内存浪费和OOM。具体采样率(inSampleSize)的计算,请看BitmapOptionsUtil

2、通过martix进行压缩(改变Bitmap大小)。

val matrix = Matrix()
matrix.setScale(0.1f, 0.1f)
val bmpMatrixJpg = Bitmap.createBitmap(
    bmpOriginJpg, 0, 0, bmpOriginJpg.getWidth(),
    bmpOriginJpg.getHeight(), matrix, true
)
// Bitmap.createScaledBitmap内部也会使用Matrix进行缩放
//            val bmpMatrixJpg = Bitmap.createScaledBitmap(
//                bmpOriginJpg,
//                ScreenUtils.dip2px(this, 60f),
//                ScreenUtils.dip2px(this, 45f),
//                true
//            )
val descBmpConfigJpg =
    "height:${bmpMatrixJpg?.height},\nwidth:${bmpMatrixJpg?.width},\nallocationByteCount:${bmpMatrixJpg?.allocationByteCount}byte,\n" +
            "byteCount:${bmpMatrixJpg?.byteCount}byte,\nrowBytes:${bmpMatrixJpg?.rowBytes}byte,\ndensity:${bmpMatrixJpg?.density}"
tvMatrixJpgInfo.text = "Jpg通过Matrix压缩后的信息:$descBmpConfigJpg"
ivMatrixJpg.setImageBitmap(bmpMatrixJpg)
AI 代码解读

Matrix缩放Bitmap

由于我们设置的缩放比是0.1f,也就是宽高均是之前的1/10,所以压缩后的bitmap占用的内存大小变为原来的1/100。

这种情况适用原图大小和目标bitmap大小均已知的情况。

3、更改Bitmap.Config格式。

val option = BitmapFactory.Options()
option.inPreferredConfig = Bitmap.Config.ARGB_4444
//  option.inPreferredConfig = Bitmap.Config.RGB_565 // 对透明度没要求的话可以试一下rgb_565
val bmpBmpConfigJpg = BitmapFactory.decodeStream(assets.open("maomi.jpg"), null, option)
val descBmpConfigJpg =
    "height:${bmpBmpConfigJpg?.height},\nwidth:${bmpBmpConfigJpg?.width},\nallocationByteCount:${bmpBmpConfigJpg?.allocationByteCount}byte,\n" +
            "byteCount:${bmpBmpConfigJpg?.byteCount}byte,\nrowBytes:${bmpBmpConfigJpg?.rowBytes}byte,\ndensity:${bmpBmpConfigJpg?.density}"
tvBmpConfigJpgInfo.text = "Jpg通过Bitmap.Config压缩后的信息:$descBmpConfigJpg"
ivBmpConfigJpg.setImageBitmap(bmpBmpConfigJpg)
AI 代码解读

Bitmap.Config减少内存占用

我们将Bitmap.Config的值改为了根据输出的byteCount的值改为了Bitmap.Config.ARGB_4444,根据byteCount输出的值可以明显的看到,bitmap的内存大小减少了一半。

如果对透明度没要求的话可以试一下Bitmap.Config.RGB_565

这种情况适用于对图片分辨率要求不高的情况。

通过Bitmap#compress方法压缩,质量压缩。

还有一种很重要的压缩方式,通过Bitmap#compress方法,修改quality的值,来改变Bitmap生成的字节流的大小。这种方法不会改变Bitmap占用的内存大小。

质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的。图片的长,宽,像素都不变,那么bitmap所占内存大小是不会变的。这里改变的是bitmap对应的字节数组的大小,适合去传递二进制的图片数据,比如微信分享。

val bytearray = getBytesFromCompressBitmap(bmpOriginJpg, 32 * 1024)
val bmpQualityJpg = BitmapFactory.decodeByteArray(bytearray, 0, bytearray.size)
val descQualityJpg =
    "height:${bmpQualityJpg.height},\nwidth:${bmpQualityJpg.width},\nallocationByteCount:${bmpQualityJpg.allocationByteCount}byte,\n" +
            "byteCount:${bmpQualityJpg.byteCount}byte,\nrowBytes:${bmpQualityJpg.rowBytes}byte,\ndensity:${bmpQualityJpg.density},\n" +
            "bytearray:${bytearray.size}"
tvQualityJpgInfo.text = "Jpg进行Quality压缩后的信息:$descQualityJpg"
ivQualityJpg.setImageBitmap(bmpQualityJpg)
AI 代码解读
/**
 * 将Bitmap的字节流压缩为目标大小
 * @targetSize  单位为Byte
 */
private fun getBytesFromCompressBitmap(
    bitmap: Bitmap,
    targetSize: Int
): ByteArray {
    val baos = ByteArrayOutputStream()
    var quality = 100
    bitmap.compress(Bitmap.CompressFormat.PNG, quality, baos)
    var bytes = baos.toByteArray()
    while (bytes.size > targetSize && quality >= 5) {
        quality -= 5
        if (quality < 0) {
            quality = 0
        }
        // 重置,不然会累加
        baos.reset()
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
        bytes = baos.toByteArray()
    }
    try {
        baos.close()
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return bytes
}
AI 代码解读

质量压缩

可以看到,质量压缩不会改变原有bitmap的大小,它改变的是通过Bitmap#compress方法的字节流。

具体开发过程中,可以根据需要自行选择合适的方式。

项目地址

tinyvampirepudge/AndroidStudy

具体页面地址:提供了jpg和png两种图片格式的demo。

BitmapCompressActivity

参考

How to get FileInputStream to File in assets folder

bitmap的六种压缩方式,Android图片压缩

目录
打赏
0
0
0
0
463
分享
相关文章
PyTorch CUDA内存管理优化:深度理解GPU资源分配与缓存机制
本文深入探讨了PyTorch中GPU内存管理的核心机制,特别是CUDA缓存分配器的作用与优化策略。文章分析了常见的“CUDA out of memory”问题及其成因,并通过实际案例(如Llama 1B模型训练)展示了内存分配模式。PyTorch的缓存分配器通过内存池化、延迟释放和碎片化优化等技术,显著提升了内存使用效率,减少了系统调用开销。此外,文章还介绍了高级优化方法,包括混合精度训练、梯度检查点技术及自定义内存分配器配置。这些策略有助于开发者在有限硬件资源下实现更高性能的深度学习模型训练与推理。
651 0
基于内存高效算法的 LLM Token 优化:一个有效降低 API 成本的技术方案
本文探讨了在构建对话系统时如何通过一种内存高效算法降低大语言模型(LLM)的Token消耗和运营成本。传统方法中,随着对话深度增加,Token消耗呈指数级增长,导致成本上升。
121 7
基于内存高效算法的 LLM Token 优化:一个有效降低 API 成本的技术方案
Android内存优化之图片优化
本文主要探讨Android开发中的图片优化问题,包括图片优化的重要性、OOM错误的成因及解决方法、Android支持的图片格式及其特点。同时介绍了图片储存优化的三种方式:尺寸优化、质量压缩和内存重用,并详细讲解了相关的实现方法与属性。此外,还分析了图片加载优化策略,如异步加载、缓存机制、懒加载等,并结合多级缓存流程提升性能。最后对比了几大主流图片加载框架(Universal ImageLoader、Picasso、Glide、Fresco)的特点与适用场景,重点推荐Fresco在处理大图、动图时的优异表现。这些内容为开发者提供了全面的图片优化解决方案。
快速定位并优化CPU 与 JVM 内存性能瓶颈
本文介绍了 Java 应用常见的 CPU & JVM 内存热点原因及优化思路。
814 166
Java内存管理深度剖析与优化策略####
本文深入探讨了Java虚拟机(JVM)的内存管理机制,重点分析了堆内存的分配策略、垃圾回收算法以及如何通过调优提升应用性能。通过案例驱动的方式,揭示了常见内存泄漏的根源与解决策略,旨在为开发者提供实用的内存管理技巧,确保应用程序既高效又稳定地运行。 ####
Headless Chrome 优化:减少内存占用与提速技巧
在数据驱动的时代,爬虫技术至关重要。本文聚焦 Headless Chrome 优化方案,解决传统爬虫内存占用高、效率低等问题。通过无界面模式、代理 IP等配置,显著降低资源消耗并提升速度。实际案例中,该方案用于采集汽车点评数据,性能提升明显:内存占用降低 30%-50%,页面加载提速 40%-60%。结合技术架构图与演化树,全面解析爬虫技术演进,助力高效数据采集。
157 0
Headless Chrome 优化:减少内存占用与提速技巧
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
105 0
如何快速定位并优化CPU 与 JVM 内存性能瓶颈?
|
5月前
|
Linux系统内存使用优化技巧
交换空间(Swap)的优化 禁用 Swap sudo swapoff -a 作用:这个命令会禁用系统中所有的 Swap 空间。swapoff 命令用于关闭 Swap 空间,-a 参数表示关闭 /etc/fstab 文件中配置的所有 Swap 空间。 使用场景:在高性能应用场景下,比如数据库服务器或高性能计算服务器,禁用 Swap 可以减少磁盘 I/O,提高系统性能。
185 3
如何使用内存监控工具来优化 Node.js 应用的性能
需要注意的是,不同的内存监控工具可能具有不同的功能和特点,在使用时需要根据具体工具的要求和操作指南进行正确使用和分析。
162 31
深入探索Android系统架构与性能优化
本文旨在为读者提供一个全面的视角,以理解Android系统的架构及其关键组件。我们将探讨Android的发展历程、核心特性以及如何通过有效的策略来提升应用的性能和用户体验。本文不包含常规的技术细节,而是聚焦于系统架构层面的深入分析,以及针对开发者的实际优化建议。
217 21

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问