本文译自「Packaging with Android Archive」,原文链接https://medium.com/gitconnected/packaging-with-android-archive-f5a33c48a25e,由Chirani Rajapaksha发布于2026年6月20日。
当 Android 开发者构建库模块时,输出的并非标准的 JAR 文件,而是 AAR 文件。Android 引入 AAR 格式的初衷是,JAR 文件基于 JVM 构建,并不包含 Android 特有的组件。JAR 文件可以包含编译后的字节码,但无法包含清单文件、资源文件、布局文件或原生 .so 库。
AAR 是一种 ZIP 压缩的归档文件,它将 Android 库所需的所有内容打包到一个可分发的文件中。根据Android开发者文档,AAR文件可以包含:
AndroidManifest.xml— 在构建时合并到使用该库的应用的清单文件中classes.jar— 已编译的Kotlin/Java字节码res/— Android资源,例如布局、可绘制对象和字符串assets/— 原始资源文件jni/— 已编译的本地.so库libs/— 其他JAR文件Consumer ProGuard规则
资源引用的R.txt符号
这与JAR文件有着本质区别,JAR文件仅包含已编译的类文件和元数据。当你的库需要声明权限、注册组件或发布本地代码时,这种区别就显得尤为重要,而这些都是封装硬件API(例如ARCore)的SDK的常见需求。
AAR 与 JAR:选择合适的格式
考虑一个使用 Flutter 开发的应用,其中包含一个用 Kotlin 编写的原生 Android 层。该应用程序使用 Google ARCore 进行平面检测和表面识别,例如识别摄像头在现实世界中看到的水平或垂直平面。
在典型的初始版本中,开发者会在整个 Android 代码库中直接导入 ARCore:
1 2 3 4 5 6 | |
每个涉及 AR 功能的文件都会与 ARCore 的内部类耦合,例如 Session、Frame、Plane、Config、Trackable 等等。这会带来一些实际问题:
升级 ARCore 存在风险。 ARCore API 的任何重大变更都意味着所有导入它的文件都需要修改,而且没有统一的边界来控制影响范围。
客户端会获取过多信息。 如果你将此 Android 模块交付给客户,他们可以查看和修改所有 ARCore 集成代码。这可能会暴露你希望保护的实现细节。
测试难度加大。 由于调用应用程序模块了解 ARCore 类,因此无法像使用桩代码那样轻松地将 AR 后端替换为单元测试用的桩代码。
解决方案是划定一个明确的边界。ARCore 成为一个实现细节,隐藏在一个接口之后,应用程序的其他部分无需了解底层 SDK 即可使用该接口。
模块结构设计
此模块仅包含契约。此处未声明任何 ARCore 依赖项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
app 模块依赖于 ar-facade。它调用 detector.startSession() 并接收 List<DetectedPlane>。它不会访问 com.google.ar.core.Plane 或任何其他 ARCore 类。
模块:ar-core-impl
步骤 1 — 将模块声明为 Android 库
在 ar-core-impl/build.gradle.kts 文件中,应用库插件而不是应用程序插件:
1 2 3 4 | |
1 2 3 4 5 6 7 | |
com.android.library 插件使 Gradle 生成 AAR 文件而不是 APK 文件。正如 Android Studio 文档 所述,应用此插件后,构建过程会创建 AAR 文件,而不是 APK 文件。
步骤 2 — 添加 ARCore 依赖项
1 2 3 4 | |
步骤 3 — 添加 ARCore 所需的清单条目
AAR 的 AndroidManifest.xml 文件会自动包含其所需的条目。当使用 AAR 的应用包含该 AAR 时,这些清单条目会被合并到最终的应用清单中:
1 2 | |
1 2 3 4 5 6 7 8 | |
根据 ARCore NDK 快速入门文档,uses-feature 标签会将应用在 Google Play 商店中的可见性限制在支持 ARCore 的设备上,而 meta-data 标签会将应用标记为 AR 必需,这意味着必须安装 Google Play 服务 AR 版。
由于这些条目存在于 AAR 的清单中,因此使用 AAR 的 app 模块无需声明任何 ARCore 特有的清单配置。它会继承库中的所有配置。
步骤 4 — 添加消费者 ProGuard 规则
1 2 | |
库的 Gradle 配置中的 consumerProguardFiles 设置可确保在使用库的应用运行 R8 或 ProGuard 时自动应用这些规则。Android 文档 指出,消费者 ProGuard 规则会随 AAR 文件一起打包,并应用于消费者的构建;SDK 作者控制哪些规则会被保留,而无需消费者手动管理这些规则。
步骤 5 — 构建 AAR 文件
1
| |
输出文件位于:
1
| |
在应用模块中使用 AAR 文件
边界到位后,“app”模块永远不会引用 ARCore。 Flutter 平台通道处理程序可能如下所示:
1
| |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
“app/”中的任何位置都不存在“import com.google.ar.core.*”行。
向客户端演示隔离
可以进行逆向工程。 AAR 是编译代码,而不是加密代码。 JADX 等工具可以反编译部分实现。为了提供更强大的 IP 保护,R8 混淆与激进的缩小和重命名提供了有意义的抵抗,尽管它并不能阻止坚定的逆向工程师。 C/C++ 中的本机实现更难反编译,并且可能适合特别敏感的逻辑。
原始 AAR 没有依赖项元数据。 正如 Android 文档说明,原始 AAR 文件不会声明其身份、版本或传递依赖项。对于超出简单本地集成的任何内容,建议通过 Maven 发布。
清单合并需要注意。 当 AAR 的清单合并到使用应用程序时,可能会出现冲突 - 例如,如果应用程序已经声明了具有不同设置的“uses-feature”。 Android 的清单合并工具 会自动处理大多数情况,但冲突需要使用合并规则手动解决。
参考文献
