配置 CMake

CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeLists.txt,其中包含 CMake 用于构建 C/C++ 库的命令。如果您的原生源代码尚无 CMake 构建脚本,则需要自己创建一个并包含相应的 CMake 命令。要了解如何安装 CMake,请参阅 安装和配置 NDK 和 CMake

本部分介绍构建脚本中应包含的一些基本命令,以便告知 CMake 在创建原生库时使用哪些源代码。要了解更多信息,请阅读有关 CMake 命令 的官方文档。

配置新的 CMake 构建脚本后,您需要配置 Gradle,将您的 CMake 项目包含为构建依赖项,以便 Gradle 构建并打包您的原生库以及应用的 APK。

注意:如果您的项目使用 ndk-build,则无需创建 CMake 构建脚本。您可以简单地配置 Gradle,通过提供指向您的Android.mk文件的路径来包含现有的原生库项目。

创建 CMake 构建脚本

要创建可作为 CMake 构建脚本使用的纯文本文件,请按以下步骤操作:

  1. 打开 IDE 左侧的项目窗格,并从下拉菜单中选择项目视图。
  2. 右键单击your-module的根目录,然后选择新建 > 文件

    注意:您可以在任何位置创建构建脚本。但是,在配置构建脚本时,您原生源文件和库的路径相对于构建脚本的位置。

  3. 输入“CMakeLists.txt”作为文件名,然后单击确定

您现在可以通过添加 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 能够在编译时找到您的头文件,您需要将include_directories()命令添加到您的 CMake 构建脚本中,并指定头文件的路径。

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 构建脚本中指定的名称。

Kotlin

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

Java

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

注意:如果您重命名或删除 CMake 构建脚本中的库,则需要在 Gradle 应用更改或从 APK 中删除旧版本的库之前清理项目。要清理项目,请从菜单栏中选择构建 > 清理项目

Android Studio 会自动将源文件和头文件添加到项目窗格中的cpp组。通过使用多个add_library()命令,您可以定义其他库,让 CMake 从其他源文件构建。

添加 NDK API

Android NDK 提供了一组您可能会发现有用的原生 API 和库。您可以通过在项目的CMakeLists.txt脚本文件中包含NDK 库来使用任何这些 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 架构,同时允许您仅使用所需的库版本。要将库的多个 ABI 版本添加到 CMake 构建脚本中,而无需为每个版本的库编写多个命令,您可以使用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

此命令将生成可以执行以创建 Android 可执行库(.so文件)的 Ninja 项目。CMAKE_TOOLCHAIN_FILE是使用 NDK 的 CMake 支持所必需的。对于 CMake 3.21 或更高版本,可以使用 CMake 内置的 NDK 支持,但必须使用不同的变量组,如 CMake 的跨编译 Android文档中所述。