AAPT2 (Android 资源打包工具) 是一款构建工具,Android Studio 和 Android Gradle 插件使用它来编译和打包应用的 资源。AAPT2 会解析、索引和编译资源,将其转换为针对 Android 平台优化的二进制格式。
Android Gradle 插件 3.0.0 及更高版本默认情况下启用 AAPT2。您通常无需自行调用 aapt2
。但是,如果您更喜欢使用终端和自己的构建系统而不是 Android Studio,则可以从命令行使用 AAPT2。您也可以从命令行调试与 AAPT2 相关的构建错误。为此,请在 Android SDK 构建工具 26.0.2 及更高版本中查找 AAPT2 作为独立工具。
要从命令行下载 Android SDK 构建工具,请使用 sdkmanager
并运行以下命令
sdkmanager "build-tools;build-tools-version"
下载 SDK 构建工具后,在 android_sdk/build-tools/version/
中查找 AAPT2。
由于 Android SDK 构建工具的修订版本不会经常发布,因此 SDK 构建工具中包含的 AAPT2 版本可能不是最新版本。要获取最新版本的 AAPT2,请 从 Google Maven 下载 AAPT2。
要在 Linux 或 Mac 上从命令行使用 AAPT2,请运行 aapt2
命令。在 Windows 上,请运行 aapt2.exe
命令。
AAPT2 通过启用增量编译来支持更快地编译资源。为了实现增量编译,资源处理分为两个步骤
这种分离有助于提高增量构建的性能。例如,如果单个文件发生更改,您只需要重新编译该文件。
从 Google Maven 下载 AAPT2
要获取构建工具中未捆绑的最新版本的 AAPT2,请从 Google 的 Maven 存储库下载 AAPT2,方法如下
- 在 存储库索引 中,导航到 **com.android.tools.build > aapt2**。
- 复制最新版本的 AAPT2 的名称。
将您复制的版本名称插入以下 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
在浏览器中导航到该 URL。AAPT2 将很快开始下载。
解压缩您刚刚下载的 JAR 文件。
JAR 文件应包含一个
aapt2
可执行文件和一些可执行文件依赖的库。
编译
AAPT2 支持编译所有 Android 资源类型,例如可绘制对象和 XML 文件。当您调用 AAPT2 进行编译时,请每次调用传递一个资源文件作为输入。然后,AAPT2 会解析该文件并生成一个具有 .flat
扩展名的中间二进制文件。
当传递整个目录时,即使只有一个资源发生了更改,AAPT2 也会重新编译目录中的所有文件。虽然您可以使用 --dir
标志将包含多个资源文件的资源目录传递给 AAPT2,但您不会以这种方式获得增量资源编译的好处。
输出文件类型可能因您提供的编译输入而异,如下表所示
输入 | 输出 |
---|---|
XML 资源文件(例如,位于 res/values/ 目录中的 字符串 和 样式) |
具有 *.arsc.flat 扩展名的资源表。 |
所有其他资源文件。 |
除 此外,所有 PNG 文件默认情况下都会被压缩并采用 |
AAPT2 输出的文件不是可执行文件,您必须稍后将这些二进制文件作为输入包含在链接阶段以生成 APK。但是,生成的 APK 文件不是可以直接部署到 Android 设备上的可执行文件,因为它不包含 DEX 文件,也没有签名。
编译语法
使用 compile
的一般语法如下
aapt2 compile path-to-input-files [options] -o output-directory/
在以下示例中,AAPT2 分别编译名为 values.xml
和 myImage.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_img.png.flat
。
编译选项
您可以使用 compile
命令使用多种选项,如表 2 所示。
选项 | 描述 |
---|---|
-o path
|
指定编译资源的输出路径。 这是一个 **必需的** 标志,因为您必须指定一个路径,让 AAPT2 可以输出并存储编译后的资源。 |
--dir directory
|
指定要扫描资源的目录。 虽然您可以使用此标志使用一个命令编译多个资源文件,但它会禁用增量编译的好处。因此,此标志不应用于大型项目。 |
--pseudo-localize
|
生成 伪本地化 版本的默认字符串,例如 en-XA 和 en-XB 。 |
--no-crunch
|
禁用 PNG 处理。
如果您已经处理过 PNG 文件,或者您正在创建不需要文件大小缩减的调试版本,请使用此选项。启用此选项会加快执行速度,但会增加输出文件的大小。 |
--legacy
|
将使用 AAPT 的早期版本时允许的错误视为警告。
此标志应用于意外的编译时错误。要解决在使用 AAPT2 时可能发生的已知行为更改,请阅读 使用 AAPT2 时的行为更改。 |
-zip file
|
file 是一个 ZIP 文件,其中包含要扫描资源的 res 目录。 |
-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.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 命名空间的属性,则此标志 **必需**。 |
-A directory
|
指定要包含在 APK 中的资产目录。
您可以使用此目录存储原始的、未处理的文件。要了解更多信息,请阅读 访问原始文件。 |
-R file
|
将单个 .flat 文件传递给 link ,使用 overlay 语义而不使用 <add-resource> 标签。
当您提供覆盖现有文件的资源文件时,将使用最后一个冲突的资源。 |
--package-id package-id
|
指定要用于您的应用的包 ID。
您指定的包 ID 必须大于或等于 0x7f,除非与 |
--allow-reserved-package-id
|
允许使用保留的包 ID。 保留的包 ID 通常分配给共享库,范围从 0x02 到 0x7e(含)。通过使用 此选项仅应用于 |
--java directory
|
指定要在其中生成 R.java 的目录。 |
--proguard proguard_options
|
生成 ProGuard 规则的输出文件。 |
--proguard-conditional-keep-rules
|
生成主要 DEX 的 ProGuard 规则的输出文件。 |
--no-auto-version
|
禁用自动样式和布局 SDK 版本控制。 |
--no-version-vectors
|
禁用矢量可绘制对象的自动版本控制。仅在使用 Vector Drawable 库构建 APK 时使用此标志。 |
--no-version-transitions
|
禁用过渡资源的自动版本控制。仅在使用 Transition Support 库构建 APK 时使用此标志。 |
--no-resource-deduping
|
禁用在兼容配置中具有相同值的资源的自动去重。 |
--enable-sparse-encoding
|
启用使用二叉搜索树对稀疏条目进行编码。这对于优化 APK 大小很有用,但以资源检索性能为代价。 |
-z
|
需要将标记为“建议”的字符串本地化。 |
-c config
|
提供一个以逗号分隔的配置列表。
例如,如果您依赖于支持库(它包含多种语言的翻译),您可以仅针对给定语言配置(如英语或西班牙语)过滤资源。 您必须使用一个由两个字母组成的 ISO 639-1 语言代码来定义语言配置,可选地后跟一个由小写字母“r”开头的两个字母组成的 ISO 3166-1-alpha-2 区域代码。例如,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 格式生成编译后的资源。 适合作为 |
--non-final-ids
|
使用非最终资源 ID 生成 R.java 。来自应用代码的 ID 引用不会在 kotlinc 或 javac 编译期间内联。 |
--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 的目标包的名称。
此选项应与 |
-0 extension
|
指定您不想压缩的文件的扩展名。 |
--split path:config[,config[..]]
|
根据一组配置拆分资源,以生成 APK 的不同版本。
必须指定输出 APK 的路径以及配置集。 |
--proguard-main-dex file
|
为主要 DEX 生成的 ProGuard 规则的输出文件。 |
--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
用于打印有关使用 link
命令生成的 APK 的信息。
转储语法
使用 dump
的通用语法如下
aapt2 dump sub-command filename.apk [options]
以下示例打印指定 APK 中资源表的内容
aapt2 dump resources output.apk
转储子命令
使用 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
一起使用
选项 | 描述 |
---|---|
--no-values
|
在显示资源时抑制值的输出。 |
--file file
|
指定作为参数从 APK 转储的文件。 |
-v
|
增加输出的冗余度。 |
差异
使用 diff
比较两个 APK 并识别它们之间的任何差异。
差异语法
使用 diff
的通用语法如下
aapt2 diff first.apk second.apk
diff
命令没有选项。
优化
optimize
用于在将合并的资源和 resources.arsc
打包到 APK 中之前对其进行优化。这种优化可以减少 APK 大小约 1-3%,具体取决于正在使用的资源的大小和数量。
优化语法
使用 optimize
的通用语法如下
aapt2 optimize options file[,file[..]]
以下示例优化 input.apk
中的资源并在 output.apk
中创建一个新的优化 APK。它用更紧凑的二叉搜索树替换了通常的扁平表表示,从而导致 APK 更小,但代价是检索性能
aapt2 optimize -o output.apk --enable-sparse-encoding input.apk
优化选项
您可以将以下选项与 optimize
一起使用
选项 | 描述 |
---|---|
-o path
|
指定链接资源 APK 的输出路径。
这是一个 **必需的** 标志,因为您必须指定输出 APK 的路径,该路径可以保存链接的资源。 |
-d directory
|
指定拆分的输出目录的路径。 |
-x path
|
指定 XML 配置文件的路径。 |
-p
|
打印多 APK 工件并退出。 |
--target-densities density[,density[..]]
|
指定 APK 优化的屏幕密度的逗号分隔列表。将从 APK 中删除在给定密度设备上未使用的所有资源。 |
--resources-config-path path
|
指定包含资源列表和对每个资源的指令的 格式: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
|
将资源名称折叠为键字符串池中的单个值。使用 no_collapse 指令在 --resources-config-path 指定的文件中免除资源。 |
--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
一起使用
选项 | 描述 |
---|---|
-o path
|
指定链接资源 APK 的输出路径。 这是一个 **必需的** 标志,因为您必须指定输出 APK 的路径,该路径可以保存链接的资源。 |
--output-format [proto|binary]
|
输出的格式。接受的值为 proto 和 binary 。如果未设置,则默认为 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 构建工具 构建的第三方库,您的应用可能会在运行时崩溃,而没有任何错误或警告。这种崩溃可能发生,因为在库创建期间,R.java
字段被声明为 final
。因此,所有资源 ID 都在库的类中内联。
AAPT2 依赖于能够在构建您的应用时重新分配库资源的 ID。如果库假定 ID 是 final
并在库 DEX 中内联它们,则会出现运行时不匹配。
要解决此错误,请联系库作者以使用最新版本的 Android SDK 构建工具重新构建库,并重新发布库。