中间件供应商建议

分发使用 NDK 构建的中间件会引发一些应用开发者无需担心的问题。预构建库将其部分实现选择强加给用户。

选择 API 级别和 NDK 版本

您的用户不能使用低于您的 minSdkVersion。如果您的用户的应用需要在 API 21 上运行,则您不能以 API 24 为目标进行构建。将库构建为低于用户使用的 API 级别是允许的。您可以以 API 16 为目标进行构建,并与使用 API 21 的用户保持兼容。

NDK 版本之间大体兼容,但偶尔会有不兼容的更改。如果您知道所有用户都使用相同版本的 NDK,最好使用与他们相同的版本。否则,请使用最新版本。

使用 STL

如果您正在编写 C++ 并使用 STL,那么如果您分发共享库,您在 libc++_sharedlibc++_static 之间的选择会影响您的用户。如果您分发共享库,则必须使用 libc++_shared 或确保您的库不暴露 libc++ 的符号。最好的方法是使用版本脚本明确声明您的 ABI 接口。

另一种不太稳健的选择是在链接时使用 -Wl,--exclude-libs,libc++_static.a -Wl,--exclude-libs,libc++abi.a。这种方法不太稳健,因为它只会隐藏明确命名的库中的符号,而对于未使用的库(库名称中的拼写错误不是错误,用户有责任更新库列表)则不会报告任何诊断信息。此方法也无法隐藏您自己的实现细节。

在 AAR 中分发原生库

Android Gradle 插件可以导入在 AAR 中分发的原生依赖项。如果您的用户使用 Android Gradle 插件,这将是他们使用您的库的最简单方法。

原生库可以通过 AGP 打包到 AAR 中。如果您的库已通过 externalNativeBuild 构建,这将是最简单的选项。

非 AGP 构建可以使用 ndkports,或遵循 Prefab 文档手动打包,在其 AAR 中创建 prefab/ 子目录。

包含 JNI 库的 Java 中间件

包含 JNI 库的 Java 库(换句话说,包含 jniLibs 的 AAR)需要注意,它们包含的 JNI 库不会与用户应用中的其他库冲突。例如,如果 AAR 包含 libc++_shared.so,但其版本与应用使用的 libc++_shared.so 不同,则只有一个会安装到 APK 中,这可能会导致不可靠的行为。

最可靠的解决方案是 Java 库包含不超过 一个 JNI 库(这对应用来说也是一个很好的建议)。包括 STL 在内的所有依赖项都应静态链接到实现库中,并且应使用版本脚本来强制执行 ABI 接口。例如,包含 JNI 库 libfooimpl.so 的 Java 库 com.example.foo 应使用以下版本脚本

LIBFOOIMPL {
global:
    JNI_OnLoad;
local:
    *;
};

本示例通过 JNI_OnLoad 使用 registerNatives,如JNI 技巧中所述,以确保公开最小的 ABI 接口并最大限度地缩短库加载时间。