深入剖析 resources.arsc:二进制资源索引表
如果你玩过 Android 逆向或者做过包体优化,一定对 resources.arsc 不陌生。这个文件不大,但乾坤都在里面。它本质上是 Android 资源索引的“字典” —— 把 R 文件里的整型 ID 对应到实际资源路径、配置和值。
我记得刚入行那会儿,用 AS 反编译一个 APK,看到这个文件完全不知道怎么下手。后来做性能优化,要裁剪多语言资源,才真正把它的结构啃了一遍。说白了,它就是一张精心设计的二进制查找表。
整体布局:一个 Chunk 嵌套的世界
resources.arsc 不是纯文本,而是由多个 Chunk(块)构成的二进制流。每个 Chunk 都有一个固定头部:
struct ResChunk_header {
uint16_t type; // chunk 类型
uint16_t headerSize; // 本 chunk 头部大小
uint32_t size; // 整个 chunk 的大小(含头部)
};
我习惯把整个文件想象成俄罗斯套娃。最外层是一个 RES_TABLE_TYPE 的 chunk,里面包了全局字符串池、资源包列表、每个包内部的条目表等等。你想想看,一个 APK 可能有多个资源包(比如默认包 + 语言包),系统在加载的时候顺着一级一级找下去。
aapt2 dump resources 可以直观看到这些 chunk 的嵌套关系。我自己写过一个解析脚本,调试时特别方便。核心数据结构:三驾马车
要理解 resources.arsc,抓住三个主题就够了:
- 全局字符串池 (Global String Pool) – 存放所有包名、资源类型名、资源项名等文本。
- 资源包 (Package) – 每个包有自己的类型表和资源表。
- 资源条目 (Entry) 与值 (Value) – 最终定位到具体的资源数据。
我曾经被一个 bug 卡了两天:多语言切换后部分字符串没生效。后来用 aapt2 dump --values 一看,原来是资源包里某个 配置限定符 写错了,导致系统 fallback 到了默认值。所以了解这些结构,不只是理论,实战中排查问题很管用。
包内结构:类型表 → 条目表 → 值
每个 Package chunk 内部有一个 类型表 (Type Table),类型表下面挂载着多个 类型块 (Type Chunk)。一个类型块对应一种资源类型(比如 layout、drawable、string),里面又包含 配置列表 (Config List) 和 条目列表 (Entry List)。
我画了张图帮你理清这个关系:
资源值与配置限定符
资源值不一定是字符串。可以是颜色、尺寸、Bool、整数,甚至是对布局文件的引用。每个值会关联一个 ResValue 结构:
struct Res_value {
uint16_t size;
uint8_t dataType; // 比如 0x03 是字符串,0x10 是颜色
uint16_t reserved;
uint32_t data; // 实际数值或字符串池索引
};
我最头疼的是 配置限定符。一个 string 条目可能对应多个值,比如 app_name 在 en-rUS 和 zh-rCN 下内容不同。系统加载时根据设备的语言、屏幕密度等,在 Config List 里找到最匹配的那个值。我曾经因为一个限位符写成了 mdpi 而不是 mdpi-v4,导致某个机型上图片始终加载不对。嗯,从那以后我检查限定符都会用 aapt2 diff 仔细核对。
实战解析:用 aapt2 dump
想知道自己的 APK 里到底有哪些资源?一条命令就够:
aapt2 dump resources your-app.apk
输出会列出所有包、类型、条目以及它们对应的值和配置。我建议你把结果重定向到文件里,然后搜一下 string 看看多语言资源是否冗余。这是包体优化的第一步。
shrinkResources + minifyEnabled),但 arsc 里的条目并不会自动压缩,甚至可能残留未使用的资源 ID。这时候你就得手动分析 arsc 里的 Entry 列表。总结一下这张表的精髓
| 层次 | 作用 | 个人经验 |
|---|---|---|
| 全局字符串池 | 存储包名、类型名、条目名等文本 | 可以在这里检查是否有冗余字符串 |
| Package | 每个包是一个隔离域,ID 不同 | 一般只有一个 0x7f 包,AAB 可能有多个 |
| Type / Config | 资源类型 + 限定符组合 | 写多语言要用 config 分离,别写在一个 values 里 |
| Entry / Value | 最终资源的值 | 小心 dataType,解析不对会崩溃 |
好了,关于 resources.arsc 的核心结构就这些。你只要把 Chunk 层次搞清楚,再结合 aapt2 或其他工具多看几遍,以后看 APK 包体就跟看自家书架一样熟悉。