AAPT2简介:
- AAPT2(Android Asset Packaging Tool)是一种构建工具,Android Studio 和 Android Gradle 插件使用它来编译和打包应用的资源。AAPT2 会解析资源、为资源编制索引,并将资源编译为针对 Android 平台进行过优化的二进制格式。
- Android Gradle 插件 3.0.0 及更高版本默认情况下会启用 AAPT2,
- 我们可以使用AAPT2的命令行来编译连接资源,
aapt2命令行的使用场景
- 在使用apktool二次打包修改、融合代码资源文件后,需要重新编译链接生成资源索引,即生成R文件,在回编译之前需使用AAPT2编译(compile)、链接(link)再次生成R.java文件,在使用jdk的javac、cvf 命令分别生成class、jar,再使用dx或R8命令行工具生成dex,最后使用baksmali工具生成samli文件合并到母包smali中,这样就做到了资源文件的真正融合
- 我们知道Android Studio能生成aab(Android App Bundle )文件供Google Play上传aab包使用,其主要是使用了bundletool工具的命令行,对命令行进行封装,使得生成aab非常简单,如果是我们自己使用bundletool命令行生成aab文件,那么我们就需要使用到AAPT2的--proto-format标记来编译、链接应用的资源,生成protobuf(Google 的协议缓冲区格式:Protocol Buffers | Google Developers) 格式的清单和资源,再使用bundletool的build-bundle命令生成aab,生成的aab再使用jarsigner签名后就可以上传到Google Paly了
- 如果你的应用或游戏有上架海外的需求,就需要提供aab包,而上架国内的各大渠道提供的是apk包,国内一般都是聚合渠道,然后使用打包工具出具体的渠道包,聚合渠道也是可以聚合海外渠道的,海外渠道如果要生成aab包,且使用打包工具而非Andorid Studio出包的话,那么就需要用到AAPT2了,在使用bundletool压缩资源之前,使用AAPT2生成protobuf 格式的清单和资源就可以了
- 我们生成的apk信息如何查看呢,AAPT的的转储(dump)命令就派上用场了,
dump
用于输出有关您使用link
命令生成的 APK 的信息 - AAPT2对资源的格式和语法要求更严格了,aapt之前报警告的地方,在aapt2下进行编译可能就直接报错了,所以我们写代码引入资源文件尽量规范
AAPT2获取方式
- 一般我们在
android_sdk/build-tools/version/
目录下就能找到 AAPT2 - 也可以从 Google 的 Maven 代码库下载最新的 AAPT2版本
AAPT2的常用命令汇总
下面的命令和使用说明来自google的aapt2文档:AAPT2 | Android 开发者 | Android Developers
后面我会使用部分命令来做一些实践,解决我们上面遇到的一些使用场景问题,另写成文章
-
编译(compile)
- AAPT2 支持编译所有 Android 资源类型,如可绘制对象和 XML 文件。调用 AAPT2 进行编译时,每次调用都应传递一个资源文件作为输入。然后,AAPT2 会解析该文件并生成一个扩展名为
.flat
的中间二进制文件,这种方式一般用于增量编译,速度很快,AS里面的封装应该就是使用此方式,我们也可以使用--dir
标记将包含多个资源文件的资源目录传递给 AAPT2来编译目录中的所有文件,并指定输出文件为.zip -
输出文件的类型可能会因您为编译提供的输入而异。下表对此进行了说明:
输入 输出 XML 资源文件(如 String 和 Style),它们位于 res/values/
目录下。以 *.arsc.flat
作为扩展名的资源表。其他所有资源文件。 除 res/values/
目录下的文件以外的其他所有文件都将转换为扩展名为*.flat
的二进制 XML 文件。此外,默认情况下,所有 PNG 文件都会被压缩,并采用*.png.flat
扩展名。如果选择不压缩 PNG,您可以在编译期间使用--no-crunch
选项。AAPT2 输出的文件不是可执行文件,稍后您必须在链接阶段添加这些二进制文件作为输入来生成 APK。但是,所生成的 APK 文件不是可以立即部署在 Android 设备上的可执行文件,因为它不包含 DEX 文件(已编译的字节码)且未签名。
-
编译语法:aapt2 compile path-to-input-files [options] -o output-directory/
编译选项
您可以将多个选项与
compile
命令搭配使用,如下表中所示:选项 说明 -o path
指定已编译资源的输出路径。 这是一个必需的标记,因为您必须指定 AAPT2 可将已编译的资源输出并存储到其中的目录的路径。
--dir directory
指定要在其中搜索资源的目录。 虽然您可以使用此标记通过一个命令编译多个资源文件,但这样就无法获得增量编译的优势,因此不建议对大型项目使用。
--pseudo-localize
生成默认字符串的伪本地化版本,如 en-XA 和 en-XB。 --no-crunch
停用 PNG 处理。 如果您已处理 PNG 文件,或者要创建不需要减小文件大小的调试 build,则可使用此选项。启用此选项可以加快执行速度,但会增大输出文件大小。
--legacy
将使用早期版本的 AAPT 时允许的错误视为警告。 此标记应用于意外的编译时错误。如需解决在使用 AAPT2 时可能遇到的已知行为变化,请参阅 AAPT2 中的行为变化。
-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.flat
和values_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
命令搭配使用:选项 说明 -o path
指定链接的资源 APK 的输出路径。 这是一个必需的标记,因为您必须指定可以存放链接资源的输出 APK 的路径。
--manifest file
指定要构建的 Android 清单文件的路径。 这是一个必需的标记,因为清单文件中包含有关您应用的基本信息(如软件包名称和应用 ID)。
-I
提供平台的 android.jar 或其他 APK(如 framework-res.apk
)的路径,这在构建功能时可能很有用。 如果您要在资源文件中使用带有android
命名空间(例如android:id
)的属性,则必须使用此标记。-A directory
指定要包含在 APK 中的资产目录。 您可以使用此目录存储未处理的原始文件。如需了解详情,请参阅访问原始文件。
-R file
传递要链接的单个 .flat 文件,使用 overlay
语义,而不使用<add-resource>
标记。如果您提供与现有文件重叠(扩展或修改现有文件)的资源文件,系统会使用最后提供的冲突资源。
--package-id package-id
指定要用于应用的软件包 ID。 除非与
--allow-reserved-package-id
结合使用,否则您指定的软件包 ID 必须大于或等于 0x7f。--allow-reserved-package-id
允许使用保留的软件包 ID。 保留的软件包 ID 是指通常分配给共享库的 ID,范围从 0x02 到 0x7e(包含端点值)。通过使用
--allow-reserved-package-id
,您可以分配属于保留的软件包 ID 范围内的 ID。此选项只能用于最低 SDK 版本为 26 或更低版本的软件包。
--java directory
指定要在其中生成 R.java
的目录。--proguard proguard_options
为 ProGuard 规则生成输出文件。 --proguard-conditional-keep-rules
为主 dex 的 ProGuard 规则生成输出文件。 --no-auto-version
停用自动样式和布局 SDK 版本控制。 --no-version-vectors
停用矢量可绘制对象的自动版本控制。 仅当使用矢量可绘制对象库构建 APK 时,才能使用此选项。 --no-version-transitions
停用转换资源的自动版本控制。 仅当使用转换支持库构建 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
中的版本代码(整数)。--compile-sdk-version-name compile-sdk-version-name
指定没有版本名称时要注入 AndroidManifest.xml
中的版本名称。--proto-format
以 Protobuf 格式生成已编译的资源。 适合作为 bundle tool 的输入,用于生成 Android App Bundle。
--non-final-ids
使用非最终资源 ID 生成 R.java
(在 kotlinc/javac 编译期间,系统不会内嵌从应用的代码对这些 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
更改插桩的目标软件包的名称。 它应与
--rename-manifest-package
结合使用。-0 extension
指定您不想压缩的文件的扩展名。
--split path:config[,config[..]]
根据一组配置拆分资源,以生成另一个版本的 APK。 您必须指定输出 APK 的路径和一组配置。
-v
可提高输出的详细程度。 转储
dump
用于输出有关您使用link
命令生成的 APK 的信息。例如,以下命令的输出结果为所指定 APK 的资源表中的内容:aapt2 dump resources output.apk
转储语法
使用
dump
的一般语法如下:aapt2 dump sub-command filename.apk [options]
转储子命令
您需要使用
dump
命令指定以下子命令之一:子命令 说明 apc
输出在编译期间生成的 AAPT2 容器(APC)的内容。 badging
输出从 APK 的清单中提取的信息。 configurations
输出 APK 中的资源使用的每项配置。 packagename
输出 APK 的软件包名称。 permissions
输出从 APK 的清单提取的权限。 strings
输出 APK 的资源表字符串池的内容。 styleparents
输出 APK 中使用的样式的父项。 resources
输出 APK 的资源表的内容。 xmlstrings
输出 APK 的已编译 xml 中的字符串。 xmltree
输出 APK 的已编译 xml 树。 转储选项
您可以将以下选项与
dump
搭配使用:选项 说明 --no-values
禁止在显示资源时输出值。 --file file
将文件指定为要从 APK 转储的参数。 -v
提高输出的详细程度。 使用 AAPT2 时的行为变化
在 AAPT2 之前,AAPT 是 Android 资源打包工具的默认版本,现在已被弃用。虽然 AAPT2 应该直接就可以处理旧版项目,但本节介绍了一些您应该注意的行为变化。
Android 清单中的元素层次结构
在以前的 AAPT 版本中,嵌套在 Android 清单中的错误节点上的元素会被忽略或引发警告。例如,请参考以下示例:
<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="foo" parent="bar"> <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="foo" parent="bar"> <item type="attr" name="my_attr">@color/pink</item> </style>
此外,在声明
<style>
元素时,其父项也必须为样式资源类型。否则,您将会遇到类似于以下内容的错误:Error: (...) invalid resource type 'attr' for parent of style
带有 ForegroundLinearLayout 的 Android 命名空间
ForegroundLinearLayout
包含三个属性:foregroundInsidePadding
、android:foreground
和android:foregroundGravity
。请注意,与其他两个属性不同,foregroundInsidePadding
未包含在android
命名空间中。在以前的 AAPT 版本中,当您使用
android
命名空间定义foregroundInsidePadding
属性时,编译器会以静默方式忽略该属性。当使用 AAPT2 时,编译器会提前捕获该属性并引发以下编译错误:Error: (...) resource android:attr/foregroundInsidePadding is private
如需解决此问题,只需将
android:foregroundInsidePadding
替换为foregroundInsidePadding
。@ 资源引用符号的误用
当您遗漏或错误放置资源引用符号 (
@
) 时,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 重新构建该库,然后重新发布该库。