2. 图片的基本存储格式与内存占用分析
做移动端开发这么多年,我见过太多因为图片格式选错而导致的内存崩溃。说白了,一张图片从文件变成屏幕上的像素,中间要经历"解码"这个过程,而不同的存储格式,直接影响了解码后的内存占用和加载速度。这一节,我们就来拆开揉碎看看,各种图片格式背后到底藏着什么门道。
1. 为什么存储格式如此重要?
先问一个问题:同样一张 1080×1920 的照片,存成 JPEG 只有 300KB,存成 PNG 却要 2MB,那它们在内存里占的空间一样吗?
答案是:不一定。因为存储格式决定的是"文件如何编码",而内存占用取决于"解码后的像素数据"。但格式不同,解码器的行为不同,加载过程中的峰值内存和最终渲染内存会有显著差异。我在项目里曾经因为用 PNG 做列表头像,导致列表滑动时频繁 OOM,换成 WebP 后直接降了 40% 的内存——这就是格式的威力。
核心结论:存储格式影响文件大小、解码速度、以及加载过程中的临时内存分配,最终影响 App 的稳定性和流畅度。
2. 主流图片格式一览
目前移动端常用的图片格式就那么几种,我整理了一个表格,方便你对比。
| 格式 | 压缩方式 | 透明度 | 文件大小 | 解码后内存(同等尺寸) | 典型场景 |
|---|---|---|---|---|---|
| JPEG | 有损 | 不支持 | 小 | 4 Bytes/像素 (ARGB_8888) | 照片、大图 |
| PNG | 无损 | 支持 | 大 | 4 Bytes/像素 (ARGB_8888) | 图标、UI 元素 |
| WebP (有损) | 有损 | 支持 | 极小 | 4 Bytes/像素 (ARGB_8888) | Web 图、列表图 |
| WebP (无损) | 无损 | 支持 | 中等 | 4 Bytes/像素 (ARGB_8888) | UI 元素、动图 |
| HEIF | 有损/无损 | 支持 | 极小 | 4 Bytes/像素 (ARGB_8888) | iOS 照片、高压缩场景 |
| GIF | 无损 (索引色) | 支持 (1位) | 中等 | 1~4 Bytes/像素 (调色板) | 动图、表情 |
| BMP | 无压缩 | 不支持 | 极大 | 3~4 Bytes/像素 | Windows 遗留系统 |
看到没?解码后的内存占用其实差不多,但文件大小和加载过程中的临时内存差异很大。这就像搬家——箱子(文件)大小不同,但东西(像素数据)搬进新家后占的面积是一样的。
3. 内存占用是怎么算出来的?
不管你用什么格式存储,图片一旦解码到内存里,就变成了原始的像素矩阵。计算公式很简单:
内存占用(字节) = 宽度(像素) × 高度(像素) × 每像素字节数(BPP)
在 Android 里,最常见的是 Bitmap.Config.ARGB_8888,每个像素占 4 个字节。RGB_565 是 2 个字节,但颜色精度差一些。
一个小案例: 一张 1080×1920 的图片,用 ARGB_8888 加载:
1080 × 1920 × 4 = 8,294,400 字节 ≈ 7.9 MB
一张图就吃掉将近 8MB 内存。如果你的列表页一次加载 20 张,就是 160MB——不 OOM 才怪。
4. 不同格式在加载过程中的内存差异
虽然解码后的最终内存差不多,但加载过程中的峰值内存差异很大。我实测过一组数据:
| 格式 | 文件大小 | 解码峰值内存 | 解码耗时 | 最终Bitmap内存 |
|---|---|---|---|---|
| JPEG (Q=85) | 320 KB | ~12 MB | 35 ms | 7.9 MB |
| PNG-32 | 1.8 MB | ~16 MB | 68 ms | 7.9 MB |
| WebP (有损, Q=80) | 180 KB | ~8 MB | 22 ms | 7.9 MB |
| HEIF | 150 KB | ~9 MB | 28 ms | 7.9 MB |
你会发现:WebP 和 HEIF 在峰值内存和加载速度上明显占优。PNG 因为需要处理过滤器和压缩数据,解码时会多分配一些临时缓冲区。
5. 实战中的格式选择策略
我在多个项目里总结了一套"格式选择三板斧",分享给你:
- 照片类大图 → WebP 有损(或 HEIF)
肉眼几乎看不出差别,文件体积减少 30%~50%,解码更快。 - UI 图标、插画 → WebP 无损(或 PNG)
需要透明通道,WebP 无损比 PNG 文件小 20%~30%。 - 动图 → WebP 动图(替代 GIF)
WebP 动图支持 24 位色 + 8 位透明,文件大小只有 GIF 的 1/3。
⚠️ 我曾经踩过一个坑: 在一个社交 App 里,用户上传的头像全部用 PNG 存储。结果首页 Feed 流加载 20 个头像,内存直接飙到 120MB。后来我改成 WebP 有损压缩(质量 85%),文件小了 60%,解码峰值内存也降了 40%。关键是用户根本看不出画质差异——选对格式,事半功倍。
6. 你需要记住的几个关键数字
- ARGB_8888:4 字节/像素 —— 默认选项,通用但最重
- RGB_565:2 字节/像素 —— 无透明通道,适合照片类
- Alpha_8:1 字节/像素 —— 只有透明通道,做遮罩用
- 1080×1920 的图:ARGB_8888 下约 7.9 MB,RGB_565 下约 3.9 MB
- WebP 相比 JPEG:文件小 30%~50%,解码峰值内存低 20%~30%
一句话总结: 图片的内存占用 = 尺寸 × 像素格式。存储格式影响的是"怎么把文件变成像素"这个过程的开销。选 WebP 或 HEIF,就是选了一条更省内存、更快的解码路径。