1. 程式人生 > >android studio中使用第三方so庫(終結版)

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庫就講完了,希望對大家有所幫助。