分发使用 NDK 构建的中间件会引发一些应用开发者无需担心的问题。预构建库将其部分实现选择强加给用户。
选择 API 级别和 NDK 版本
您的用户不能使用低于您的 minSdkVersion。如果您的用户的应用需要在 API 21 上运行,则您不能以 API 24 为目标进行构建。将库构建为低于用户使用的 API 级别是允许的。您可以以 API 16 为目标进行构建,并与使用 API 21 的用户保持兼容。
NDK 版本之间大体兼容,但偶尔会有不兼容的更改。如果您知道所有用户都使用相同版本的 NDK,最好使用与他们相同的版本。否则,请使用最新版本。
使用 STL
如果您正在编写 C++ 并使用 STL,那么如果您分发共享库,您在 libc++_shared
和 libc++_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 接口并最大限度地缩短库加载时间。