android studio中使用第三方so庫(終結版)
安卓開發中,經常會用到第三方C++庫,然後不知道是我對系統不熟悉的原因,還是安卓支援C++開發本來就比較弱,反正其中踩了很多坑。
android studio 版本 3.0.1, 在PC上安裝android studio3.1.2,就是配置不成功,後面再筆記本上安裝android studio3.0.1, 安裝成功,本以為在PC上重新安裝android studio3.0.1應該可以成功,但實際還是不行,因該是哪裡配置問題,解除安裝時又解除安裝不乾淨。
操作步驟:
1、在android studio中建立一個工程,修改工程名,選擇支援C++,其他使用系統預設引數
支援c++的工程中,多了一個CPP目錄,裡面一個native-lib.cpp,同時也多了一個CMakelists.txt
後續我們要修改下面幾個檔案:CMakelists.txt,app下面的build.gradle,native-lib.cpp,MainActivity.java
2、拷貝so和include檔案到工程目錄中
在app目錄下建立一個 libs目錄,然後在libs目錄中,根據so的編譯的cpu架構,建立該架構名的目錄,我的是armeabi-v7a,將so檔案拷貝到該目錄。
同時在libs下面建立include目錄,把標頭檔案放這個目錄下;
注:目前有七種安卓的cpu架構:1.armeabi ,2.armeabi-v7a,3.arm64-v8a,4.x86,5.x86_64,6.MIPS,7.MIPS64,目前主流的是armeabi-v7a,CPU架構具體介紹請百度;
3、修改CMakelists.txt檔案
先設定一個環境變數:set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs)
這個裡面感覺是最坑的,下面逐個介紹
1)include目錄配置,效果就是 -I 引數,有兩個方法
INCLUDE_DIRECTORY(${distribution_DIR}/include) # 感覺無效,設定後並沒有引數效果
target_include_directories(native-lib PRIVATE
${distribution_DIR}/include) # 這個測試是OK的
2)載入so
2.1)方法一
add_library( ebase_define
SHARED
IMPORTED)
set_target_properties( ebase_define
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libebase_define.so)
#set_target_properties:這裡有個坑,後面再講
target_link_libraries(
native-lib
ebase_define #在target_link_libraries中增加ebase_define 效果是增加 -l 引數
${log-lib})
2.2)方法二:使用-L引數
set(LD_LIBRARY_PATH ${distribution_DIR}/armeabi-v7a) # 測試無效,從命令列中來看,並沒有增加-L引數
LINK_DIRECTORIES(${distribution_DIR}/armeabi-v7a) # 測試無效,從命令列中來看,並沒有增加-L引數
target_link_libraries(
native-lib
ebase_define #在target_link_libraries中增加ebase_define 效果是增加 -l 引數
${log-lib})
測試了很多網上用的方法,結果都沒有引數 -L 引數,感覺這是一個bug,此路不通過,有大神知道怎麼增加 -L 引數請不吝賜教。
其實上面兩個方法都有問題,我們暫且先用方法一繼續。
4、修改build.gradle
1)android/defaultConfig 中增加:
externalNativeBuild {
cmake {
cppFlags ""
abiFilters "armeabi-v7a"
}
}
2)android 中增加:
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
jniLibs.srcDirs = ['libs'] 從測試效果來看,主要是打APK軟體包時有用,沒有加這個,apk中不會有libebase_define.so庫
5、修改native-lib.cpp,這個可以參考native-lib.cpp現有程式碼以及C++語法,編寫自己需要的介面
6、修改MainActivity.java
class MainActivity 中增加:
static {
System.loadLibrary("ebase_define"); // 增加的
System.loadLibrary("native-lib"); // 自動生成的
}
說明:其實System.loadLibrary("ebase_define");都可以不需要,載入native-lib庫時,由於native-lib依賴ebase_define,會自動載入ebase_define庫。
7、編譯、測試
通過上面的方法,編譯成功,但是執行時出錯,開啟app後閃退,日誌顯示:
java.lang.UnsatisfiedLinkError: dlopen failed: library "../../../../libs/armeabi-v7a/libebase_define.so" not found
這錯誤真是讓人頭疼,為啥執行時,會載入這個目錄的so呢,這個目錄在哪裡設定的?
最後經過千山萬水,終於發現這是前面挖的坑:
set_target_properties( ebase_define
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libebase_define.so)
這個目錄就是跟${distribution_DIR}/armeabi-v7a/libebase_define.so目錄一致,原來android studio將編譯和執行的目錄搞成一個了,這難道是個bug?
8、解決問題
沒辦法,只能繼續回到編譯時增加 -L 引數的方法,這樣編譯的時候,lib庫就不需要路徑了,
有人說使用find_library,測試還是無效
find_library(LIBHELLO_PATH ebase_define F:/paas_msp/android_prj/HelloC/app/libs/armeabi-v7a NO_DEFAULT_PATH)
link_libraries(${LIBHELLO_PATH})
找了各種方法,還是沒有搞定,問題還要繼續搞。
去掉add_library和set_target_properties兩個函式後,編譯時報錯,找不到 -lebase_define ,這是預想到的,在錯誤的命令列中,找到了一個-L引數,這個引數是系統增加的,-LD:/Android/android-sdk-windows/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a
其中D:/Android/android-sdk-windows是android sdk的安裝目錄,於是我將庫放到了D:/Android/android-sdk-windows/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a目錄下
終於編譯成功了,並且測試也是OK的,不再報java.lang.UnsatisfiedLinkError: dlopen failed: library "../../../../libs/armeabi-v7a/libebase_define.so" not found 這個錯誤。
上面僅僅是通過規避的方法搞定的,增加-L引數才是正道,不知道哪個大神能夠指點一二。
後面使用開源專案時還遇到一個問題:開源專案一般編譯出來的so帶版本號,如openssl編譯後的so有版本號, libssl.so.1,因為編譯時需要用libopenssl.so,我把這個檔名改成了libopenssl.so,編譯是通過了,但是執行時找的是openssl.so.1,linux下通常是用link檔案搞定的,但是windows又不支援link檔案。這讓我想到前面的載入so的java程式碼,我是讓系統自動載入的,沒有顯示呼叫載入 openssl庫,在 System.loadLibrary("native-lib"); 的前面增加System.loadLibrary("openssl"); 嘿嘿,問題解決了。
好了,android studio中使用第三方so庫就講完了,希望對大家有所幫助。