移植 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 版本也保持一致。

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