AAPT2

AAPT2(Android Asset Packaging Tool)是一个构建工具,Android Studio 和 Android Gradle 插件使用它来编译和打包应用的 资源。AAPT2 会解析、索引和编译资源,并将它们转换为针对 Android 平台优化的二进制格式。

Android Gradle 插件 3.0.0 及更高版本默认启用 AAPT2。通常您无需自行调用 aapt2。但是,如果您更喜欢使用终端和自己的构建系统而不是 Android Studio,则可以从命令行使用 AAPT2。您还可以从命令行调试与 AAPT2 相关的构建错误。为此,请在 Android SDK Build Tools 26.0.2 及更高版本中找到 AAPT2 作为独立工具。

要从命令行下载 Android SDK Build Tools,请使用 sdkmanager 并运行以下命令

sdkmanager "build-tools;build-tools-version"

下载 SDK Build Tools 后,在 android_sdk/build-tools/version/ 中找到 AAPT2。

由于 Android SDK Build Tools 的修订版本不会经常发布,因此 SDK Build Tools 中包含的 AAPT2 版本可能不是最新版本。要获取最新版本的 AAPT2,请 从 Google Maven 下载 AAPT2

要在 Linux 或 Mac 上从命令行使用 AAPT2,请运行 aapt2 命令。在 Windows 上,请运行 aapt2.exe 命令。

AAPT2 通过启用增量编译来支持更快的资源编译。要实现增量编译,资源处理分为两个步骤

  • 编译:将资源文件编译成二进制格式。
  • 链接:合并所有已编译的文件并将它们打包到单个包中。

这种分离有助于提高增量构建的性能。例如,如果单个文件发生更改,则只需要重新编译该文件。

从 Google Maven 下载 AAPT2

要获取构建工具中未捆绑的最新版本的 AAPT2,请按照以下步骤从 Google 的 Maven 存储库下载 AAPT2

  1. 存储库索引 中,导航到 com.android.tools.build > aapt2
  2. 复制最新版本 AAPT2 的名称。
  3. 将您复制的版本名称插入以下 URL 并指定您的目标操作系统:https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2/ aapt2-version/aapt2-aapt2-version- [windows | linux | osx].jar

    例如,要下载适用于 Windows 的 3.2.0-alpha18-4804415 版本,请使用:https://dl.google.com/dl/android/maven2/com/android/tools/build/aapt2/ 3.2.0-alpha18-4804415/aapt2-3.2.0-alpha18-4804415-windows.jar

  4. 在浏览器中导航到该 URL。AAPT2 将很快开始下载。

  5. 解压缩您刚刚下载的 JAR 文件。

    JAR 文件应包含一个 aapt2 可执行文件和一些可执行文件依赖的库。

编译

AAPT2 支持编译所有 Android 资源类型,例如可绘制对象和 XML 文件。当您调用 AAPT2 进行编译时,请每次调用都传递一个资源文件作为输入。然后,AAPT2 会解析该文件并生成一个扩展名为 .flat 的中间二进制文件。

当传递整个目录时,即使只有一个资源发生更改,AAPT2 也会重新编译目录中的所有文件。虽然您可以使用 --dir 标志将包含多个资源文件的资源目录传递给 AAPT2,但这样做无法获得增量资源编译的优势。

输出文件类型可能因您提供的编译输入而异,如下表所示。

表 1. 编译的输入和输出文件类型

输入 输出
XML 资源文件,例如位于 res/values/ 目录中的 字符串样式 资源表,其扩展名为 *.arsc.flat
所有其他资源文件。

res/values/ 目录下的文件外,所有其他文件都将转换为具有 *.flat 扩展名的二进制 XML 文件。

此外,默认情况下所有 PNG 文件都会被压缩并采用 *.png.flat 扩展名。如果您选择不压缩 PNG,可以在编译期间使用 --no-crunch 选项。

AAPT2 输出的文件不是可执行文件,您必须稍后将这些二进制文件作为输入包含在链接阶段中以生成 APK。但是,生成的 APK 文件不是您可以立即部署到 Android 设备上的可执行文件,因为它不包含 DEX 文件且未签名。

编译语法

使用 compile 的通用语法如下所示

aapt2 compile path-to-input-files [options] -o output-directory/

在以下示例中,AAPT2 分别编译名为 values.xmlmyImage.png 的资源文件。

aapt2 compile project_root/module_root/src/main/res/values-en/strings.xml -o compiled/
aapt2 compile project_root/module_root/src/main/res/drawable/myImage.png -o compiled/

如表 1 所示,输出文件的名称取决于输入文件名及其父目录的名称。

对于前面使用 strings.xml 文件作为输入的示例,aapt2 会自动将输出文件命名为 values-en_strings.arsc.flat。但是,存储在 drawable 目录中的已编译 drawable 文件命名为 drawable_img.png.flat

编译选项

您可以与 compile 命令一起使用多个选项,如表 2 所示。

表 2. 编译命令选项

选项 描述
-o path

指定已编译资源的输出路径。

这是一个必需标志,因为您必须指定 AAPT2 可以输出和存储已编译资源的目录路径。

--dir directory

指定要扫描资源的目录。

虽然您可以使用此标志通过一个命令编译多个资源文件,但这会禁用增量编译的优势。因此,此标志不应用于大型项目。

--pseudo-localize 生成默认字符串的 伪本地化 版本,例如 en-XAen-XB
--no-crunch 禁用 PNG 处理。

如果您已经处理过 PNG 文件或正在创建不需要减小文件大小的调试版本,请使用此选项。启用此选项可加快执行速度,但会增加输出文件大小。

--legacy 将使用早期版本的 AAPT 时允许的错误视为警告。

此标志应用于意外的编译时错误。要解决在使用 AAPT2 时可能发生的已知行为更改,请阅读 使用 AAPT2 时的行为更改

-zip file file 是包含要扫描资源的 res 目录的 ZIP 文件。
-output-text-symbols file 生成一个包含指定资源符号的文本文件文件.
-preserve-visibility-of-styleables 如果指定,则对 styleables 应用与所有其他资源相同的可见性规则。否则,所有 styleables 都将设为公开。
-visibility [public|private|default|] 将已编译资源的可见性设置为指定的级别。
-trace-folder folder 生成一个 systrace JSON 跟踪片段到指定的文件夹.
-source-path path 将已编译资源文件的源文件路径设置为路径.
-h 显示工具帮助。
-v 启用详细日志记录。

在链接阶段,AAPT2 会合并编译阶段生成的所有中间文件,例如资源表、二进制 XML 文件和处理后的 PNG 文件,然后将这些文件打包到一个 APK 中。此外,在此阶段还可以生成其他辅助文件,例如 R.java 和 ProGuard 规则文件。但是,生成的 APK 不包含 DEX 字节码且未签名。您无法将此 APK 部署到设备上。

如果您没有使用 Android Gradle 插件从 命令行构建您的应用,则可以使用其他命令行工具,例如 d8 将 Java 字节码编译成 DEX 字节码,以及 apksigner 来签署您的 APK。

使用 link 的通用语法如下所示

aapt2 link path-to-input-files [options] -o
outputdirectory/outputfilename.apk --manifest AndroidManifest.xml

在以下示例中,AAPT2 合并了两个中间文件 drawable_Image.flatvalues_values.arsc.flat,以及 AndroidManifest.xml 文件。AAPT2 将结果与 android.jar 文件链接,该文件保存 android 包中定义的资源。

 aapt2 link -o output.apk
 -I android_sdk/platforms/android_version/android.jar
    compiled/res/values_values.arsc.flat
    compiled/res/drawable_Image.flat --manifest /path/to/AndroidManifest.xml -v

您可以将以下选项与 link 命令一起使用。

表 3. 链接命令选项

选项 描述
-o path

指定链接资源 APK 的输出路径。

这是一个必需标志,因为您必须指定可以保存链接资源的输出 APK 的路径。

--manifest file

指定要构建的 Android 清单文件的路径。

这是一个必需标志,因为清单文件包含有关您的应用的基本信息,例如包名称和应用 ID。

-I

提供平台的 android.jar 或其他 APK(如 framework-res.apk)的路径,这在构建功能时可能很有用。

如果您在资源文件中使用具有 android 命名空间的属性,则此标志是必需的。
-A directory 指定要包含在 APK 中的 assets 目录。

您可以使用此目录存储原始的、未处理的文件。要了解更多信息,请阅读 访问原始文件

-R file 将单个 .flat 文件传递给 link,使用 overlay 语义而不使用 <add-resource> 标记。

当您提供的资源文件覆盖现有文件时,将使用最后一个冲突的资源。

--package-id package-id 指定要用于应用的包 ID。

您指定的包 ID 必须大于或等于 0x7f,除非与 --allow-reserved-package-id 结合使用。

--allow-reserved-package-id

允许使用保留的包 ID。

保留的包 ID 是通常分配给共享库且在 0x02 到 0x7e(含)范围内的 ID。通过使用 --allow-reserved-package-id,您可以分配落在保留包 ID 范围内的 ID。

此选项仅应用于 min-sdk 版本为 26 或更低的包。

--java directory 指定在其中生成 R.java 的目录。
--proguard proguard_options 生成 ProGuard 规则的输出文件。
--proguard-conditional-keep-rules 为主要 DEX 生成 ProGuard 规则的输出文件。
--no-auto-version 禁用样式和布局 SDK 版本的自动设置。
--no-version-vectors 禁用矢量 drawable 的自动版本设置。仅在使用 Vector Drawable 库构建 APK 时使用此标志。
--no-version-transitions 禁用过渡资源的自动版本设置。仅在使用 Transition Support 库构建 APK 时使用此标志。
--no-resource-deduping 禁用跨兼容配置具有相同值的资源的自动重复数据删除。
--enable-sparse-encoding 启用使用二叉搜索树对稀疏条目进行编码。这对于优化 APK 大小很有用,但会以资源检索性能为代价。
-z 需要本地化标记为“建议”的字符串。
-c config 提供一个用逗号分隔的配置列表。

例如,如果您依赖于支持库(包含多种语言的翻译),则可以仅过滤给定语言配置(如英语或西班牙语)的资源。

您必须通过一个两位的 ISO 639-1 语言代码来定义语言配置,可选地后跟一个两位的 ISO 3166-1-alpha-2 区域代码,前面带有小写字母“r”。例如,en-rUS。

--preferred-density density 允许 AAPT2 选择最接近的匹配密度并删除所有其他密度。

您的应用可以使用多种像素密度限定符,例如 ldpi、hdpi 和 xhdpi。当您指定首选密度时,AAPT2 会在资源表中选择并存储最接近的匹配密度,并删除所有其他密度。

--output-to-dir 将 APK 内容输出到 -o 指定的目录。

如果您在使用此标志时遇到任何错误,您可以通过升级到 Android SDK Build Tools 28.0.0 或更高版本 来解决这些错误。

--min-sdk-version min-sdk-version 设置要用于 AndroidManifest.xml 的默认最低 SDK 版本。
--target-sdk-version target-sdk-version 设置要用于 AndroidManifest.xml 的默认目标 SDK 版本。
--version-code version-code 指定要注入到 AndroidManifest.xml 中的版本代码(如果不存在)。

--version-name version-name 指定要注入到 AndroidManifest.xml 中的版本名称,如果不存在则注入。
--revision-code revision-code 指定要注入到 AndroidManifest.xml 文件中的修订版本代码,如果不存在则注入。
--replace-version 如果指定了 --version-code--version-name--revision-code,则这些值将替换清单中已有的任何值。默认情况下,如果清单已定义这些属性,则不会发生任何更改。
--compile-sdk-version-nacodeme compile-sdk-version-name 指定要注入到 AndroidManifest.xml 文件中的版本代码,如果不存在则注入。
--compile-sdk-version-name compile-sdk-version-name 指定要注入到 AndroidManifest.xml 文件中的版本名称,如果不存在则注入。
--proto-format 以 Protobuf 格式生成编译后的资源。

适合作为 bundletool 生成 Android App Bundle 的输入。

--non-final-ids 生成 R.java,其中包含非最终的资源 ID。在 kotlincjavac 编译期间,不会内联来自应用代码的 ID 引用。
--emit-ids path 在给定路径处发出一个文件,其中包含资源类型名称列表及其 ID 映射。这适合与 --stable-ids 一起使用。
--stable-ids outputfilename.ext 使用 --emit-ids 生成的包含资源类型名称列表及其已分配 ID 的文件。

此选项允许在链接时删除或添加新资源时,分配的 ID 保持稳定。

--custom-package package_name 指定要生成 R.java 的自定义 Java 包。
--extra-packages package_name 生成相同的 R.java 文件,但使用不同的包名。
--add-javadoc-annotation annotation 向所有生成的 Java 类添加 JavaDoc 注解。
--output-text-symbols path 生成一个文本文件,其中包含指定文件中 R 类的资源符号。

必须指定输出文件的路径。

--auto-add-overlay 允许在覆盖中添加新资源,而无需使用 <add-resource> 标签。
--rename-manifest-package manifest-package 重命名 AndroidManifest.xml 文件中的包。
--rename-instrumentation-target-package instrumentation- target-package 更改 instrumentation 的目标包的名称。

此选项应与 --rename-manifest-package 一起使用。

-0 extension

指定您不想压缩的文件的扩展名。

--split path:config[,config[..]] 根据一组配置拆分资源,以生成 APK 的不同版本。

必须指定输出 APK 的路径以及配置集。

--proguard-main-dex file 为生成的 ProGuard 主 DEX 规则生成输出文件。
--proguard-minimal-keep-rules 生成一组最小的 ProGuard 保留规则。
--no-resource-removal 禁用自动删除没有默认值的资源。仅在构建运行时资源覆盖包时使用此选项。
-x 指定使用包标识符 0x01 的旧版标志。
--product products-list 指定要保留的产品名称的逗号分隔列表。
--no-xml-namespaces AndroidManifest.xml 文件和 res/* 中的 XML 二进制文件中删除 XML 命名空间前缀和 URI 信息。
--shared-lib 生成共享的 Android 运行时库。
--static-lib 生成静态 Android 库。
--no-static-lib-packages 将所有库资源合并到应用的包下。
--no-proguard-location-reference 防止 ProGuard 规则文件引用源文件。
--private-symbols package-name package-name 指定在为私有符号生成 R.java 时要使用的包名。如果未指定,则公共和私有符号使用应用的包名。
--override-styles-instead-of-overlaying 导致在 -R 资源中定义的样式替换以前的定义,而不是合并它们。
--rename-resources-package package-name 将资源表中的包重命名为 package-name
--no-compress 不压缩任何资源。
--keep-raw-values 保留 XML 文件中的原始属性值。
--no-compress-regex regular-expression 不压缩与 regular-expression 匹配的扩展名。使用 $ 符号表示行尾。使用区分大小写的 ECMAScript 正则表达式语法。
--warn-manifest-validation 将清单验证错误视为警告。
--exclude-configs qualifier[,qualifier[..]] 排除其配置包含指定限定符的资源的值。
--debug-mode 在清单的应用程序节点中插入 android:debuggable="true",即使在生产设备上也能使应用程序可调试。
--strict-visibility 不允许具有不同可见性级别的覆盖。
--exclude-sources 在以 Protobuf 格式生成资源时不序列化源文件信息。
--trace-folder folder 生成 systrace JSON 跟踪片段到指定的 folder
--merge-only 仅合并资源,而不验证资源引用。此标志应仅与 --static-lib 标志一起使用。
-h 显示帮助菜单。
-v 启用输出的详细程度。

Dump

dump 用于打印有关使用 link 命令生成的 APK 的信息。

Dump 语法

使用 dump 的一般语法如下所示

aapt2 dump sub-command filename.apk [options]

以下示例打印来自指定 APK 的资源表的内容

aapt2 dump resources output.apk

Dump 子命令

使用 dump 命令指定以下子命令之一

表 4. Dump 子命令

子命令描述
apc 打印编译期间生成的 AAPT2 容器 (APC) 的内容。
badging 打印从 APK 的清单中提取的信息。
configurations 打印 APK 中资源使用的每个配置。
overlayable 打印 APK 的可覆盖资源。
packagename 打印 APK 的包名。
permissions 打印从 APK 的清单中提取的权限。
strings 打印 APK 的资源表字符串池的内容。
styleparents 打印 APK 中使用的样式的父级。
resources 打印 APK 的资源表的内容。
xmlstrings 打印 APK 的已编译 XML 中的字符串。
xmltree 打印 APK 的已编译 XML 的树。

Dump 选项

将以下选项与 dump 一起使用

表 5. Dump 选项

选项描述
--no-values 在显示资源时抑制值的输出。
--file file 指定一个文件作为要从 APK 中转储的参数。
-v 增加输出的详细程度。

Diff

使用 diff 比较两个 APK 并识别它们之间的任何差异。

Diff 语法

使用 diff 的一般语法如下所示

aapt2 diff first.apk second.apk

diff 命令没有选项。

Optimize

optimize 用于在合并的资源和 resources.arsc 打包到 APK 中之前对其运行优化。此优化可以减少 APK 大小约 1-3%,具体取决于正在使用的资源的大小和数量。

Optimize 语法

使用 optimize 的一般语法如下所示

aapt2 optimize options file[,file[..]]

以下示例优化 input.apk 中的资源并在 output.apk 中创建一个新的优化后的 APK。它用更紧凑的二叉搜索树替换通常的扁平表表示形式,从而以检索性能为代价减小 APK 大小

aapt2 optimize -o output.apk --enable-sparse-encoding input.apk

Optimize 选项

您可以将以下选项与 optimize 一起使用

表 6. Optimize 选项

选项描述
-o path 指定链接资源 APK 的输出路径。

这是一个必需标志,因为您必须指定可以保存链接资源的输出 APK 的路径。

-d directory 指定拆分输出目录的路径。
-x path 指定 XML 配置文件的路径。
-p 打印多 APK 工件并退出。
--target-densities density[,density[..]] 指定 APK 针对其优化的屏幕密度的逗号分隔列表。从 APK 中删除在给定密度的设备上不会使用的所有资源。
--resources-config-path path

指定包含资源列表和每个资源指令的 resources.cfg 文件的路径。

格式:type/resource_name#[directive][,directive]

-c config[,config[..]] 指定要包含的配置的逗号分隔列表。默认为所有配置。
--split path:config[,config[..]] 根据一组配置拆分资源,以生成 APK 的不同版本。

必须指定输出 APK 的路径以及配置集。

--keep-artifacts artifact[,artifact[..]] 指定要保留的工件的逗号分隔列表。如果未指定,则保留所有工件。
--enable-sparse-encoding 启用使用二叉搜索树对稀疏条目进行编码。此选项可用于优化 APK 大小,但会影响资源检索性能。
--collapse-resource-names 将资源名称折叠为键字符串池中的单个值。使用 --resources-config-path 指定的文件中的 no_collapse 指令免除资源。
--shorten-resource-paths

缩短 APK 内部资源的路径。
--resource-path-shortening-map path 指定输出旧资源路径到缩短路径映射的路径。
-v 增加输出的详细程度。
-h 显示工具帮助。

转换

默认情况下,AAPT compile 命令将资源编译成适合 APK 的二进制格式。通过指定 --proto-format,也可以指定适合 AAB 的 protobuf 格式。 convert 命令在两种格式之间转换 APK。

转换语法

convert 的通用语法如下所示

aapt2 convert -o output-file options file[,file[..]]

以下示例转换 input.apk 中的资源,并在 output.apk 中创建一个新的 APK,其中包含 protobuf 格式的资源。它用更紧凑的二叉搜索树替换了通常的扁平表表示,从而使 APK 更小,但代价是检索性能下降。

aapt2 convert -o output.apk --output-format proto --enable-sparse-encoding input.apk

转换选项

将以下选项与 convert 一起使用

表 7. 转换选项

选项描述
-o path

指定链接资源 APK 的输出路径。

这是一个必需标志,因为您必须指定可以保存链接资源的输出 APK 的路径。

--output-format [proto|binary] 输出格式。接受的值为 protobinary。未设置时,默认为 binary
--enable-sparse-encoding 启用使用二叉搜索树对稀疏条目进行编码。此选项可用于优化 APK 大小,但会影响资源检索性能。
--keep-raw-values 保留 XML 文件中的原始属性值。
-v 增加输出的详细程度。
-h 显示工具帮助。

守护进程模式

AAPT 2.19 版引入了用于发出命令的守护进程模式。守护进程模式允许您在一个 AAPT 会话中输入多个命令。

守护进程语法

使用以下命令启动守护进程模式

aapt2 daemon

守护进程模式运行后,您可以输入命令。命令的每个参数都必须位于单独的一行,命令末尾必须有一行空行。通过键入 Control+D 退出守护进程模式。

考虑以下各个 compile 命令

aapt2 compile project_root/module_root/src/main/res/values-en/strings.xml -o compiled/
aapt2 compile project_root/module_root/src/main/res/drawable/myImage.png -o compiled/

这些命令可以在守护进程模式下输入为

aapt2 daemon
Ready
compile
project_root/module_root/src/main/res/values-en/strings.xml
-o
compiled/

Done
compile
project_root/module_root/src/main/res/drawable/myImage.png
-o
compiled/

Done
^D
Exiting daemon

守护进程模式选项

守护进程模式的唯一选项是 --trace-folder folder,它会生成一个 systrace JSON 跟踪片段到指定的 folder

版本

使用 version 命令确定您正在使用的 AAPT2 版本

aapt2 version
Android Asset Packaging Tool (aapt) 2.19-8678579

使用 AAPT2 时行为变化

在 AAPT2 之前,AAPT 是 Android Asset Packaging Tool 的默认版本,现在已弃用。虽然 AAPT2 应该可以立即与旧项目一起使用,但本节介绍了一些您应该注意的行为变化。

Android 清单中的元素层次结构

在早期版本的 AAPT 中,嵌套在 AndroidManifest.xml 文件中不正确节点中的元素要么被忽略,要么导致警告。例如,请考虑以下示例

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.myname.myapplication">
   <application
       ...
       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
           <action android:name="android.intent.action.CUSTOM" />
       </activity>
   </application>
</manifest>

早期版本的 AAPT 将简单地忽略放置错误的 <action> 标记。

使用 AAPT2 时,您会收到以下错误

AndroidManifest.xml:15: error: unknown element <action> found.

要解决此问题,请确保您的清单元素嵌套正确。有关更多信息,请阅读 应用清单概述

资源声明

您不能再从 name 属性指示资源的类型。以下示例错误地声明了 attr 资源项

<style name="childStyle" parent="parentStyle">
    <item name="attr/my_attr">@color/pink</item>
</style>

以这种方式声明资源类型会导致以下构建错误

Error: style attribute 'attr/attr/my_attr (aka my.package:attr/attr/my_attr)'
not found.

要解决此错误,请使用 type="attr" 显式声明类型

<style name="childStyle" parent="parentStyle">
  <item type="attr" name="my_attr">@color/pink</item>
</style>

此外,在声明 <style> 元素时,其父元素也必须是样式资源类型。否则,您会收到类似于以下内容的错误

Error: (...) invalid resource type 'attr' for parent of style

@ 资源引用符号的错误使用

当您省略或错误地放置资源引用符号(@)时,AAPT2 会引发构建错误。例如,如果您在指定样式属性时省略了该符号

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  ...
  <!-- Note the missing '@' symbol when specifying the resource type. -->
  <item name="colorPrimary">color/colorPrimary</item>
</style>

构建模块时,AAPT2 会引发以下构建错误

ERROR: expected color but got (raw string) color/colorPrimary

此外,如果您在从 android 命名空间访问资源时错误地包含了该符号

...
<!-- When referencing resources from the 'android' namespace, omit the '@' symbol. -->
<item name="@android:windowEnterAnimation"/>

构建模块时,AAPT2 会引发以下构建错误

Error: style attribute '@android:attr/windowEnterAnimation' not found

库的错误配置

如果您的应用依赖于使用旧版 Android SDK Build Tools 构建的第三方库,则您的应用可能会在运行时崩溃,而不会显示任何错误或警告。此崩溃可能会发生,因为在库创建期间,R.java 字段被声明为 final。因此,所有资源 ID 都内联在库的类中。

AAPT2 依赖于能够在构建应用时重新分配库资源的 ID。如果库假设 ID 为 final 并将它们内联到库 DEX 中,则会出现运行时不匹配。

要解决此错误,请联系库作者使用最新版本的 Android SDK Build Tools 重新构建库,并重新发布库。