Android NDK开发详解之针对中间件供应商的建议

Android NDK开发详解之针对中间件供应商的建议

      • [选择 API 级别和 NDK 版本](#选择 API 级别和 NDK 版本)
      • [使用 STL](#使用 STL)
      • [在 AAR 中分发原生库](#在 AAR 中分发原生库)
      • [包含 JNI 库的 Java 中间件](#包含 JNI 库的 Java 中间件)

分发使用 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 界面(这也有助于将实现细节保持私有状态)。例如,一个简单的算术库可能拥有以下版本脚本:

注意:如果您要分发静态库,无论是选择静态 STL 还是共享 STL 都无关紧要,因为静态库中未关联任何内容。用户可以关联他们在应用中选择的任何内容。但用户必须关联一些内容,即使是针对仅限 C 的使用方。因此,请务必将这一点记录为必须采取的措施,并记录用于构建的 NDK 版本,以防 STL 版本之间存在不兼容的情况。

LIBMYMATH {
global:
    add;
    sub;
    mul;
    div;
    # C++ symbols in an extern block will be mangled automatically. See
    # https://stackoverflow.com/a/21845178/632035 for more examples.
    extern "C++" {
        "pow(int, int)";
    }
local:
    *;
};

首选方式应为版本脚本,因为这是控制符号可见性的最可靠方式。这对于所有共享库(无论是否是中间件)来说都是最佳做法,因为这可以防止您的实现细节被公开,并能够缩短加载时间。

另一个可靠性较低的方式是在关联时使用 -Wl,--exclude-libs,libc++_static.a -Wl,--exclude-libs,libc++abi.a。这种方式的可靠性较低是因为它只会隐藏库中明确指定的符号,并且不会为未使用的库报告诊断信息(库名称中的拼写错误不属于错误,并且用户应负责及时更新库列表)。此方式也不会隐藏您自己的实现细节。

在 AAR 中分发原生库

注意:本部分将介绍如何将 C/C++ API 分发给用户。如果原生库属于您的 Java API 的实现细节,请参阅包含 JNI 库的 Java 中间件部分。

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

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

非 AGP build 可以使用 ndkports,或按照 Prefab 文档中的说明执行手动打包,以便创建 AAR 的 prefab/ 子目录。

包含 JNI 库的 Java 中间件

包含 JNI 库(即包含 jniLibs 的 AAR)的 Java 库需要小心谨慎地处理,这样它们包含的 JNI 库才不会与用户应用中的其他库发生冲突。例如,如果 AAR 包含的 libc++_shared.so 版本不同于应用使用的 libc++_shared.so 版本,那么只有其中一个版本会安装到 APK,并且这可能会引发不可靠的行为。

警告:错误 141758241:较旧版本的 Android Gradle 插件目前不会诊断这种错误。系统将任意选择一个同名库打包到 APK 中。

最可靠的解决方案是确保 Java 库中的 JNI 库不超过一个(这个建议也适用于应用)。包括 STL 在内的所有依赖项都应静态关联到实现库,并且应通过版本脚本强制执行 ABI 界面。例如,包含 JNI 库 libfooimpl.so 的 Java 库 com.example.foo 应使用以下版本脚本:

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

本示例按照 JNI 提示中所述的说明,在 JNI_OnLoad 中使用 registerNatives 来确保尽量少公开 ABI 界面,并最大限度缩短库的加载时间。

相关推荐
小小李程序员1 小时前
LRU缓存
java·spring·缓存
cnsxjean1 小时前
SpringBoot集成Minio实现上传凭证、分片上传、秒传和断点续传
java·前端·spring boot·分布式·后端·中间件·架构
hadage2331 小时前
--- stream 数据流 java ---
java·开发语言
《源码好优多》1 小时前
基于Java Springboot汽配销售管理系统
java·开发语言·spring boot
sunly_2 小时前
Flutter:启动屏逻辑处理02:启动页
android·javascript·flutter
果冻~2 小时前
【nodejs表单数据的中间件&错误级别的中间件】
中间件
小林想被监督学习2 小时前
Java后端如何进行文件上传和下载 —— 本地版
java·开发语言
Erosion20202 小时前
SPI机制
java·java sec
Sgq丶2 小时前
Android Studio 配置 proto
android·ide·android studio
逸风尊者3 小时前
开发也能看懂的大模型:RNN
java·后端·算法