設定 CMake

CMake 建構指令碼是純文字檔案,您必須命名為 CMakeLists.txt,並且包含 CMake 用來建構 C/C++ 資料庫的指令。如果原生來源沒有 CMake 建構指令碼,您必須自行建立並加入適當的 CMake 指令。如要瞭解如何安裝 CMake,請參閱安裝並設定 NDK 和 CMake

本節說明在建構指令碼中應加入的一些基本指令,以便告知 CMake 建立原生資料庫時要使用哪些來源。詳情請參閱 CMake 指令的官方說明文件。

設定新的 CMake 建構指令碼後,您必須設定 Gradle,將 CMake 專案當成建構依附元件加入,以便 Gradle 用您應用程式的 APK 建構及封裝您的原生資料庫。

注意:如果您的專案使用 NDK 版本,則不需要建立 CMake 建構指令碼。您只需要提供 Android.mk 檔案路徑,就能 設定 Gradle 來加入現有的原生資料庫專案。

建立 CMake 建構指令碼

如要建立可做為 CMake 建構指令碼的純文字檔案,請按照下列步驟操作:

  1. 開啟 IDE 左側的「Project」(專案) 窗格,然後從下拉式選單中選取「Project」(專案) 檢視畫面。
  2. your-module 的根目錄上按一下滑鼠右鍵,然後選取「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 )

提示:與 CMake 建立來源檔案的原生資料庫的做法類似,您可以使用 add_executable() 指令,讓 CMake 從這些來源檔案建立執行檔。不過,您可以選擇從原生來源建構執行檔,並且建構原生資料庫來封裝到 APK 中,以滿足大部分的專案需求。

使用 add_library() 在 CMake 版本指令碼中新增來源檔案或資料庫時,Android Studio 也會在同步處理專案後,在「專案」檢視畫面中顯示相關標頭檔案。不過,為了讓 CMake 在編譯期間找出標頭檔案,您必須在 CMake 版本指令碼中加入 include_directories() 指令並指定路徑:

add_library(...)

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

CMake 為資料庫檔案命名的慣例如下:

liblibrary-name.so

舉例來說,如果您在建構指令碼中指定「native-lib」做為共用資料庫的名稱,CMake 就會建立名為 libnative-lib.so 的檔案。不過,當您以 Java 或 Kotlin 程式碼載入這個資料庫時,請使用 CMake 版本指令碼中指定的名稱:

Groovy

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

Kotlin

companion object {
    init {
        System.loadLibrary("native-lib");
    }
}

注意:如果您重新命名或移除 CMake 建構指令碼中的資料庫,請先清除專案,再讓 Gradle 套用變更,或將舊版資料庫從 APK 中移除。如要清理專案,請依序選取選單列中的「Build」(建立) >「Clean Project」(清理專案)。

Android Studio 會自動將來源檔案和標頭新增至「專案」窗格中的 cpp 群組。透過使用多個 add_library() 指令,即可定義其他資料庫,讓 CMake 從其他來源檔案建構。

新增 NDK API

Android NDK 提供的一系列原生 API 和資料庫,可能對您有所幫助。你可以在專案的 CMakeLists.txt 指令碼檔案中加入 NDK 資料庫,藉此使用這些 API。

Android 平台上已有預先建構的 NDK 資料庫,因此您不需要另外建立這些資料庫,或將其封裝至 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} )

如要將預先建構的資料庫封裝至 APK,您必須使用 sourceSets 區塊 手動設定 Gradle,加入 .so 檔案的路徑。建立 APK 後,您可以使用 APK 分析工具來驗證 Gradle 封裝至您 APK 中的資料庫。

包含其他 CMake 專案

如要建構多個 CMake 專案並在 Android 專案中加入這些專案的輸出內容,您可以使用一個 CMakeLists.txt 檔案做為頂層 CMake 建構指令碼 (之後您將 Gradle 連結至此),以及新增其他 CMake 專案做為該建構指令碼的依附元件。下列頂層 CMake 建構指令碼使用 add_subdirectory() 指令指定其他 CMakeLists.txt 檔案做為建構依附元件,然後將其連結至輸出內容,如同其他預先內建的資料庫。

# Sets lib_src_DIR to the path of the target CMake project.
set( lib_src_DIR ../gmath )

# Sets lib_build_DIR to the path of the desired output directory.
set( lib_build_DIR ../gmath/outputs )
file(MAKE_DIRECTORY ${lib_build_DIR})

# Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory( # Specifies the directory of the CMakeLists.txt file.
                  ${lib_src_DIR}

                  # Specifies the directory for the build outputs.
                  ${lib_build_DIR} )

# Adds the output of the additional CMake build as a prebuilt static
# library and names it lib_gmath.
add_library( lib_gmath STATIC IMPORTED )
set_target_properties( lib_gmath PROPERTIES IMPORTED_LOCATION
                       ${lib_build_DIR}/${ANDROID_ABI}/lib_gmath.a )
include_directories( ${lib_src_DIR}/include )

# Links the top-level CMake build output against lib_gmath.
target_link_libraries( native-lib ... lib_gmath )

透過指令列呼叫 CMake

使用下列指令來呼叫 CMake,並在 Android Studio 以外的位置產生 Ninja 專案:

cmake
-Hpath/to/cmakelists/folder
-Bpath/to/generated/ninja/project/debug/ABI
-DANDROID_ABI=ABI                               // For example, arm64-v8a
-DANDROID_PLATFORM=platform-version-string      // For example, android-16
-DANDROID_NDK=android-sdk/ndk/ndk-version
-DCMAKE_TOOLCHAIN_FILE=android-sdk/ndk/ndk-version/build/cmake/android.toolchain.cmake
-G Ninja

這個指令會產生可執行的 Ninja 專案,方便您建立 Android 執行檔資料庫 (.so檔案)。需要 CMAKE_TOOLCHAIN_FILE 才能使用 NDK 的 CMake 支援。針對 CMake 3.21 以上版本,您可以改用 CMake 的內建 NDK 支援,但必須按照 CMake 的Android 跨平台程式碼編譯說明文件所述,使用其他不同的變數群組。