resource.arsc 是android二进制资源索引文件,以restable_header开头(headersize必须为12),仅存资源id映射元信息,不含实际资源数据;解析需严格按字节对齐、相对偏移和utf-16 le编码处理字符串池与res_value。

Resource.arsc 文件结构到底长什么样
Resource.arsc 不是普通 ZIP 内的资源表,而是 Android 编译后生成的、高度压缩且内存友好的二进制资源索引文件。它不包含实际资源数据(比如 PNG 或字符串内容),只存资源 ID 到值偏移/配置/类型等元信息。解析失败的根源往往在于:误把它当 XML 解析,或跳过对 ResTable_header 和 ResStringPool_header 的字节对齐校验。
关键点:
- 整个文件以
ResTable_header开头,headerSize字段必须为 12(不是 sizeof(ResTable_header)!因为含 padding) - 所有 header 结构体末尾都有隐式对齐(通常是 4 字节),读取时必须按
headerSize而非 C++ struct sizeof 跳转 -
ResStringPool_header中的stringsStart是相对于 pool 起始地址的偏移,不是文件绝对偏移 - 字符串池采用 UTF-16 编码,但低字节在前(LE),且每个字符串长度字段是 uint16_t —— 直接 reinterpret_cast
会乱码
用 C++ 读取 ResTable_package 和资源项索引
一个 APK 可能含多个 package(如主 App + 动态模块),ResTable_header 后紧跟若干 ResTable_package。每个 package 包含 typeStrings、keyStrings 和多组 ResTable_typeSpec + ResTable_type,这才是资源 ID 映射的核心链路。
实操建议:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 先定位到第一个
ResTable_package:跳过ResTable_header,再按packageCount循环解析;注意id字段是 package ID(0x7f 表示应用资源),不是用于计算 R.java ID 的 base -
typeStrings和keyStrings都是ResStringPool_header开头,必须分别解析两次 —— 混用会导致 type 名(如 "drawable")和 key 名(如 "ic_launcher")错位 - 每个
ResTable_typeSpec描述一种资源类型(如 string、layout)有多少个配置变体;其后的ResTable_type才真正存储具体资源值的 offset 数组,且每个ResTable_type对应一个配置(如 en-US、hdpi) - 资源值通过
Res_value结构读取:dataType决定解释方式(0x03=string,0x12=reference),data是索引或原始值 —— 若为 string 类型,data是keyStrings中的索引,不是直接字符串内容
解析 Res_value 时最容易踩的坑
Res_value 看似简单,但 data 字段含义随 dataType 剧烈变化,且部分类型(如 TYPE_DYNAMIC_REFERENCE)在旧版 aapt 生成的 arsc 中根本不会出现,导致硬编码判断崩溃。
常见错误现象:
- 读出字符串显示为乱码或空 —— 忘记用
keyStrings的data值去查字符串池,而是直接把data当作 char* 解释 - 解析 layout 资源时崩溃 —— 把
Res_value.dataType == 0x10(即TYPE_ATTRIBUTE)误认为是整数,实际它是 attribute 引用,需结合ResTable_package的resourceID域还原完整 ID - 获取不到 values-zh-rCN 下的字符串 —— 没遍历所有
ResTable_type,只读了第一个(默认配置),而中文配置可能在后续 type 中 - 64 位程序读取 32 位编译的 arsc 时
ResTable_config解析错位 —— 该结构体含大量 uint32_t 字段但无显式 padding,必须严格按 spec 字节顺序读,不能依赖 struct 内存布局
推荐最小可行解析流程(C++17)
不要试图一次性加载全部资源,先实现「给定资源名 + 配置,返回字符串值」这一闭环。以下步骤缺一不可:
- 用
std::ifstream以std::ios::binary打开文件,seekg到ResTable_header起始,验证packageCount >= 1且headerSize == 12 - 跳转到第一个
ResTable_package,读取typeStrings和keyStrings的 offset,分别解析两个字符串池(注意stringCount和styleCount) - 遍历
typeStrings找到目标 type 名(如 "string")的 index;再遍历所有ResTable_typeSpec,找到该 type index 对应的 spec,记录其entryCount - 对每个
ResTable_type(按配置匹配),检查config字段是否满足目标密度/语言,然后从entriesStart开始,按entryCount读取每个 entry offset;若 offset 非 0xFFFF,用它索引到Res_value - 若
Res_value.dataType == 0x03,用Res_value.data查keyStrings池,再用查得的 offset 读 UTF-16 字符串,最后转换为 std::u16string 或 UTF-8 std::string
复杂点不在代码量,而在每个 offset 都是“相对中的相对”—— 相对于 header、相对于 pool、相对于 entriesStart。少一次 seekg 或错用一个 size,整条链就断了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










