配置 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 中,您需要 手动配置 Gradle,使用 sourceSets 块包含指向您的 .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 进行交叉编译 文档中所述。