AndroidStudio用Cmake方式編譯NDK代碼(cmake配置.a庫)


1.cmake是什么?

CMake是一個跨平台的安裝(編譯)工具,可以用簡單的語句來描述所有平台的安裝(編譯過程)。他能夠輸出各種各樣的makefile或者project文件,能測試編譯器所支持的C++特性,類似UNIX下的automake。

 

谷歌從AndroidStudio2.2以上就添加了Cmake方式來編譯NDK代碼,並從NDK例子看出,默認編譯的方式就是cmake方式。

 

2.谷歌官方的用cmake方式編譯NDK的教程

 

谷歌從AndroidStudio2.2以上就添加了Cmake方式來編譯NDK代碼,並從NDK例子看出,默認編譯的方式就是cmake方式。

如果您希望向現有項目添加原生代碼,請執行以下步驟:

  1. 創建新的原生源文件並將其添加到您的 Android Studio 項目中。
    • 如果您已經擁有原生代碼或想要導入預構建的原生庫,則可以跳過此步驟。
  2. 創建 CMake 構建腳本,將您的原生源代碼構建到庫中。如果導入和關聯預構建庫或平台庫,您也需要此構建腳本。
    • 如果您的現有原生庫已經擁有 CMakeLists.txt 構建腳本或者使用 ndk-build 並包含 Android.mk 構建腳本,則可以跳過此步驟。
  3. 提供一個指向您的 CMake 或 ndk-build 腳本文件的路徑,將 Gradle 關聯到您的原生庫。Gradle 使用構建腳本將源代碼導入您的 Android Studio 項目並將原生庫(SO 文件)封裝到 APK 中。

配置完項目后,您可以使用 JNI 框架從 Java 代碼中訪問您的原生函數。要構建和運行應用,只需點擊 Run 從菜單欄運行應用。Gradle 會以依賴項的形式添加您的外部原生構建流程,用於編譯、構建原生庫並將其隨 APK 一起封裝。

創建新的原生源文件

要在應用模塊的主源代碼集中創建一個包含新建原生源文件的 cpp/ 目錄,請按以下步驟操作:

  1. 從 IDE 的左側打開 Project 窗格並從下拉菜單中選擇 Project 視圖。
  2. 導航到您的模塊 > src,右鍵點擊 main 目錄,然后選擇 New > Directory
  3. 為目錄輸入一個名稱(例如 cpp)並點擊 OK
  4. 右鍵點擊您剛剛創建的目錄,然后選擇 New > C/C++ Source File
  5. 為您的源文件輸入一個名稱,例如 native-lib
  6. 從 Type 下拉菜單中,為您的源文件選擇文件擴展名,例如 .cpp
    • 點擊 Edit File Types ,您可以向下拉菜單中添加其他文件類型,例如 .cxx 或 .hxx。在彈出的 C/C++ 對話框中,從 Source Extension 和 Header Extension 下拉菜單中選擇另一個文件擴展名,然后點擊 OK
  7. 如果您還希望創建一個標頭文件,請選中 Create an associated header 復選框。
  8. 點擊 OK

創建 CMake 構建腳本

如果您的原生源文件還沒有 CMake 構建腳本,則您需要自行創建一個並包含適當的 CMake 命令。CMake 構建腳本是一個純文本文件,您必須將其命名為 CMakeLists.txt。本部分介紹了您應包含到構建腳本中的一些基本命令,用於在創建原生庫時指示 CMake 應使用哪些源文件。

:如果您的項目使用 ndk-build,則不需要創建 CMake 構建腳本。提供一個指向您的 Android.mk 文件的路徑,將 Gradle 關聯到您的原生庫

要創建一個可以用作 CMake 構建腳本的純文本文件,請按以下步驟操作:

  1. 從 IDE 的左側打開 Project 窗格並從下拉菜單中選擇 Project 視圖。
  2. 右鍵點擊您的模塊的根目錄並選擇 New > File

    :您可以在所需的任意位置創建構建腳本。不過,在配置構建腳本時,原生源文件和庫的路徑將與構建腳本的位置相關。

  3. 輸入“CMakeLists.txt”作為文件名並點擊 OK

現在,您可以添加 CMake 命令,對您的構建腳本進行配置。要指示 CMake 從原生源代碼創建一個原生庫,請將 cmake_minimum_required() 和 add_library() 命令添加到您的構建腳本中:

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add.library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

 

使用 add_library() 向您的 CMake 構建腳本添加源文件或庫時,Android Studio 還會在您同步項目后在 Project 視圖下顯示關聯的標頭文件。不過,為了確保 CMake 可以在編譯時定位您的標頭文件,您需要將 include_directories() 命令添加到 CMake 構建腳本中並指定標頭的路徑:

add_library(...)

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

CMake 使用以下規范來為庫文件命名:

lib庫名稱.so

例如,如果您在構建腳本中指定“native-lib”作為共享庫的名稱,CMake 將創建一個名稱為 libnative-lib.so 的文件。不過,在 Java 代碼中加載此庫時,請使用您在 CMake 構建腳本中指定的名稱:

static {     System.loadLibrary(“native-lib”); }

:如果您在 CMake 構建腳本中重命名或移除某個庫,您需要先清理項目,Gradle 隨后才會應用更改或者從 APK 中移除舊版本的庫。要清理項目,請從菜單欄中選擇 Build > Clean Project

Android Studio 會自動將源文件和標頭添加到 Project 窗格的 cpp 組中。使用多個 add_library() 命令,您可以為 CMake 定義要從其他源文件構建的更多庫。

添加 NDK API

Android NDK 提供了一套實用的原生 API 和庫。通過將 NDK 庫包含到項目的 CMakeLists.txt 腳本文件中,您可以使用這些 API 中的任意一種。

預構建的 NDK 庫已經存在於 Android 平台上,因此,您無需再構建或將其封裝到 APK 中。由於 NDK 庫已經是 CMake 搜索路徑的一部分,您甚至不需要在您的本地 NDK 安裝中指定庫的位置 - 只需要向 CMake 提供您希望使用的庫的名稱,並將其關聯到您自己的原生庫。

將 find_library() 命令添加到您的 CMake 構建腳本中以定位 NDK 庫,並將其路徑存儲為一個變量。您可以使用此變量在構建腳本的其他部分引用 NDK 庫。以下示例可以定位 Android 特定的日志支持庫並將其路徑存儲在 log-lib 中:

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )

 

為了確保您的原生庫可以在 log 庫中調用函數,您需要使用 CMake 構建腳本中的 target_link_libraries() 命令關聯庫:

find_library(...)

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

 

NDK 還以源代碼的形式包含一些庫,您在構建和關聯到您的原生庫時需要使用這些代碼。您可以使用 CMake 構建腳本中的 add_library() 命令,將源代碼編譯到原生庫中。要提供本地 NDK 庫的路徑,您可以使用 ANDROID_NDK 路徑變量,Android Studio 會自動為您定義此變量。

以下命令可以指示 CMake 構建 android_native_app_glue.c,后者會將 NativeActivity 生命周期事件和觸摸輸入置於靜態庫中並將靜態庫關聯到 native-lib

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )

 

添加其他預構建庫

添加預構建庫與為 CMake 指定要構建的另一個原生庫類似。不過,由於庫已經預先構建,您需要使用 IMPORTED 標志告知 CMake 您只希望將庫導入到項目中:

add_library( imported-lib
             SHARED
             IMPORTED )

然后,您需要使用 set_target_properties() 命令指定庫的路徑,如下所示。

某些庫為特定的 CPU 架構(或應用二進制接口 (ABI))提供了單獨的軟件包,並將其組織到單獨的目錄中。此方法既有助於庫充分利用特定的 CPU 架構,又能讓您僅使用所需的庫版本。要向 CMake 構建腳本中添加庫的多個 ABI 版本,而不必為庫的每個版本編寫多個命令,您可以使用 ANDROID_ABI 路徑變量。此變量使用 NDK 支持的一組默認 ABI,或者您手動配置 Gradle 而讓其使用的一組經過篩選的 ABI。例如:

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

為了確保 CMake 可以在編譯時定位您的標頭文件,您需要使用 include_directories() 命令,並包含標頭文件的路徑:

include_directories( imported-lib/include/ )

:如果您希望封裝一個並不是構建時依賴項的預構建庫(例如在添加屬於 imported-lib 依賴項的預構建庫時),則不需要執行以下說明來關聯庫。

要將預構建庫關聯到您自己的原生庫,請將其添加到 CMake 構建腳本的 target_link_libraries() 命令中:

target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

在您構建應用時,Gradle 會自動將導入的庫封裝到 APK 中。您可以使用 APK 分析器驗證 Gradle 將哪些庫封裝到您的 APK 中。如需了解有關 CMake 命令的詳細信息,請參閱 CMake 文檔

 

導入.a靜態庫

自 android studio 2.2 +后就集成了ndk開發, 自帶cmake 編譯器.  編寫ndk時候,配置很簡單。再也不需要用android.mk配置文件。

新建一個帶ndk開發項目的結構是這樣的,

 

 

言歸正傳,那么要添加第三方的xx.a鏈接庫呢?

通常我們把第三方提供的h文件夾,放在cpp的include里面。這是規范,不是必須。而xxx.a庫放在src/main/jniLibs/armeabi目錄下。

本文章以添加libjsoncpp.a連接庫做例子

首先在cpp目錄下建立一個include文件夾,把jsoncpp官方提供的頭文件文件夾拷貝到include里面(我這個項目有3個鏈接庫,jsoncpp, curl , openssl,另外2個僅做參考作用,與其無關)

 

 

第二步, 在app的src目錄的main下,建立一個文件夾,jniLibs,然后在jniLibs里面再建立一個armeabi文件夾。

然后把libjsoncpp.a鏈接庫拷貝進去。

 

第三步, 動態庫與頭文件拷貝進去時候,是需要告訴編譯器做關聯的。在app目錄的src文件夾下有個CMakeLists.txt文件,我們通過它編寫配置信息。

jsoncpp頭文件所在目錄告訴編譯,在里面添加

  1. include_directories( src/main/cpp/include/jsoncpp)  

如果有多個鏈接庫,那么可以這樣
例如有三個鏈接庫,jsoncpp, currl , openssl.

 
  1. include_directories( src/main/cpp/include/jsoncpp  
  2.                      src/main/cpp/include/curl  
  3.                      src/main/cpp/include/openssl  
  4.                      )  
 

然后告訴編譯器,libjsoncppp.a在哪個目錄下,並指定鏈接庫的名稱

接着添加

  1. #添加json庫  
  2. add_library(jsoncpp STATIC IMPORTED)  
  3. set_target_properties(jsoncpp  
  4.   PROPERTIES IMPORTED_LOCATION  
  5.   ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libjsoncpp.a)  

然后修改target_link_libraries,加多一行jsoncpp

 
  1. target_link_libraries(native-lib  
  2.                       jsoncpp  
  3.                       ${log-lib})  

如果有多個,那么可以多次添加。如

  1. #添加json庫  
  2. add_library(jsoncpp STATIC IMPORTED)  
  3. set_target_properties(jsoncpp  
  4.   PROPERTIES IMPORTED_LOCATION  
  5.   ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libjsoncpp.a)  
  6.   
  7. #添加curl網絡請求  
  8. add_library(curl STATIC IMPORTED)  
  9. set_target_properties(curl  
  10.   PROPERTIES IMPORTED_LOCATION  
  11.   ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libcurl.a)  
  12.   
  13. #添加加密工具(md5, base64, des, aes , asa) part-1  
  14. add_library(crypto STATIC IMPORTED)  
  15. set_target_properties(crypto  
  16.   PROPERTIES IMPORTED_LOCATION  
  17.   ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libcrypto.a)  
  18.   
  19. #添加加密工具(md5, base64, des, aes , asa) des加密 part-2  
  20. add_library(ssl STATIC IMPORTED)  
  21. set_target_properties(ssl  
  22.   PROPERTIES IMPORTED_LOCATION  
  23.   ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libssl.a)  
 
        
  1. target_link_libraries(native-lib  
  2.                       jsoncpp  
  3.                       curl  
  4.                       crypto  
  5.                       ssl  
  6.                       ${log-lib})   

然后gradle編譯就可以使用了。是不是很簡單?

 

 

要將 Gradle 關聯到您的原生庫,您需要提供一個指向 CMake 或 ndk-build 腳本文件的路徑。在您構建應用時,Gradle 會以依賴項的形式運行 CMake 或 ndk-build,並將共享的庫封裝到您的 APK 中。Gradle 還使用構建腳本來了解要將哪些文件添加到您的 Android Studio 項目中,以便您可以從 Project 窗口訪問這些文件。如果您的原生源文件沒有構建腳本,則需要先創建 CMake 構建腳本,然后再繼續。

將 Gradle 關聯到原生項目后,Android Studio 會更新 Project 窗格以在 cpp 組中顯示您的源文件和原生庫,在 External Build Files 組中顯示您的外部構建腳本。

:更改 Gradle 配置時,請確保通過點擊工具欄中的 Sync Project  應用更改。此外,如果在將 CMake 或 ndk-build 腳本文件關聯到 Gradle 后再對其進行更改,您應當從菜單欄中選擇 Build > Refresh Linked C++ Projects,將 Android Studio 與您的更改同步。

您可以使用 Android Studio UI 將 Gradle 關聯到外部 CMake 或 ndk-build 項目:

  1. 從 IDE 左側打開 Project 窗格並選擇 Android 視圖。
  2. 右鍵點擊您想要關聯到原生庫的模塊(例如 app 模塊),並從菜單中選擇 Link C++ Project with Gradle。您應看到一個如圖 4 所示的對話框。
  3. 從下拉菜單中,選擇 CMake 或 ndk-build
    1. 如果您選擇 CMake,請使用 Project Path 旁的字段為您的外部 CMake 項目指定 CMakeLists.txt 腳本文件。
    2. 如果您選擇 ndk-build,請使用 Project Path 旁的字段為您的外部 ndk-build 項目指定 Android.mk 腳本文件。如果 Application.mk 文件與您的 Android.mk 文件位於相同目錄下,Android Studio 也會包含此文件。

    圖 4.使用 Android Studio 對話框關聯外部 C++ 項目。

  4. 點擊 OK

手動配置 Gradle

要手動配置 Gradle 以關聯到您的原生庫,您需要將 externalNativeBuild {} 塊添加到模塊級 build.gradle 文件中,並使用 cmake {} 或 ndkBuild {}對其進行配置:

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

    // Encapsulates your CMake build configurations.
    cmake {

      // Provides a relative path to your CMake build script.
      path "CMakeLists.txt"
    }
  }
}

:如果您想要將 Gradle 關聯到現有 ndk-build 項目,請使用 ndkBuild {} 塊而不是 cmake {},並提供 Android.mk 文件的相對路徑。如果 Application.mk 文件與您的 Android.mk 文件位於相同目錄下,Gradle 也會包含此文件。

指定可選配置

您可以在模塊級 build.gradle 文件的 defaultConfig {} 塊中配置另一個 externalNativeBuild {} 塊,為 CMake 或 ndk-build 指定可選參數和標志。與 defaultConfig {} 塊中的其他屬性類似,您也可以在構建配置中為每個產品風味重寫這些屬性。

例如,如果您的 CMake 或 ndk-build 項目定義多個原生庫,您可以使用 targets 屬性僅為給定產品風味構建和封裝這些庫中的一部分。以下代碼示例說明了您可以配置的部分屬性:

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use ndkBuild {}
      cmake {

        // Passes optional arguments to CMake.
        arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

        // Sets optional flags for the C compiler.
        cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"

        // Sets a flag to enable format macro constants for the C++ compiler.
        cppFlags "-D__STDC_FORMAT_MACROS"
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries to build and package for this
          // product flavor. If you don't configure this property, Gradle
          // builds and packages all shared object libraries that you define
          // in your CMake or ndk-build project.
          targets "native-lib-demo"
        }
      }
    }

    paid {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets "native-lib-paid"
        }
      }
    }
  }

  // Use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

要詳細了解配置產品風味和構建變體,請參閱配置構建變體。如需了解您可以使用 arguments 屬性為 CMake 配置的變量列表,請參閱使用 CMake 變量

指定 ABI

默認情況下,Gradle 會針對 NDK 支持的 ABI 將您的原生庫構建到單獨的 .so 文件中,並將其全部封裝到您的 APK 中。如果您希望 Gradle 僅構建和封裝原生庫的特定 ABI 配置,您可以在模塊級 build.gradle 文件中使用 ndk.abiFilters 標志指定這些配置,如下所示:

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your APK.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

在大多數情況下,您只需要在 ndk {} 塊中指定 abiFilters(如上所示),因為它會指示 Gradle 構建和封裝原生庫的這些版本。不過,如果您希望控制 Gradle 應當構建的配置,並獨立於您希望其封裝到 APK 中的配置,請在 defaultConfig.externalNativeBuild.cmake {} 塊(或 defaultConfig.externalNativeBuild.ndkBuild {} 塊中)配置另一個 abiFilters 標志。Gradle 會構建這些 ABI 配置,不過僅會封裝您在 defaultConfig.ndk{} 塊中指定的配置。

為了進一步降低 APK 的大小,請考慮配置 ABI APK 拆分,而不是創建一個包含原生庫所有版本的大型 APK,Gradle 會為您想要支持的每個 ABI 創建單獨的 APK,並且僅封裝每個 ABI 需要的文件。如果您配置 ABI 拆分,但沒有像上面的代碼示例一樣指定 abiFilters 標志,Gradle 會構建原生庫的所有受支持 ABI 版本,不過僅會封裝您在 ABI 拆分配置中指定的版本。為了避免構建您不想要的原生庫版本,請為 abiFilters 標志和 ABI 拆分配置提供相同的 ABI 列表。

 

谷歌從AndroidStudio2.2以上就添加了Cmake方式來編譯NDK代碼,並從NDK例子看出,默認編譯的方式就是cmake方式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM