移植 C/C++ 代码至 Android 平台经验总结

  算法同学提供了一个图像分析能力,使用 C/C++ 实现,依赖了 Tensorflowlite、 OpenCV 等库。要在 Android 上使用这个能力,需要进行移植。移植可分为两步,第一步是面向 Android 设备的指令集构建 C/C++ 代码以及其依赖的库,第二步是编写一个 Wrapper 封装第一步生成的库并供 JNI 调用。

  针对第一步,我们首先要知道我们要部署的 Android 设备用的是什么型号的 CPU,它支持的指令集有哪些,存储是大端还是小端,我们要构建的是 native code,没有中间解释或适配,必须是 CPU 能直接理解的,Android 把指令集有哪些,存储是大端还是小端等特性组合抽象为了 ABI(Android Binary Interface),即 arm64-v8a, armeabi-v7a。我们把 Wrapper 作为主体,算法同学的 C/C++ 代码是我们依赖,Tensorflowlite、OpenCV 是我们的间接依赖。我们要确保 prebuilt 的间接依赖的构建架构和我们要部署的 CPU 的架构(ABI)是一致的,否则无法兼容。另外注意 Android Dynamic Linker 的特性,为 Shared Library 提供 SONAME 信息,否则无法加载成功。具体的请参见问答以及问答。我的理解是,Shared Library 对另一个 Shared Library 的依赖是通过 SONAME 进行依赖,如果没有设置,Compile 阶段 SONAME 的位置的内容就变成了绝对路径,这将导致运行时加载错误。事实证明设置 SONAME 信息之后,问题解决。注意,NDK 作为工具套装,提供了检查 Shared / Static Library 的工具,在 toolchains 目录下,各种架构的库有与之对应的工具,分别位于对应的文件夹下。

  另外,NDK 中不支持 Shared Library 再去依赖 Static Library,需要将 Static Library 改为 Shared Library。参见问答。Static Library 代码会在编译阶段打进可执行文件中,而 Shared Library 是运行时加载,它 resolve 符号时不会反向的查找,只会去它依赖的还未加载的 Shared Library 里面找。参考问答,使用 gcc 编译时,静态库的顺序也很重要。

  另外附一些有用的命令,1. ndk-build V=1,可以打印 ndk-build 详细过程,比如使用 gcc 还是 clang,有没有 link 某个特定的库;2. nm -gD someSharedLibrary.so / nm someStaticLibrary.a | grep someSymobol,可以检测库里是否包含某个符号;3. ndk toolchains folder/readelf -dW someSharedLibrary.so,可以检查动态库是否包含 SONAME 信息。还有,ndk-build 和 cmake 区别其实很小,不同的 DL 来描述构建而已,使用时注意区分语法即可。并且,prebuilt 的库用 gcc 编译的,最好 ndk-build 也用 gcc,同理前者用 clang,后者也用 clang,std 版本也保持一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章