Android Studio 配置 CMake
如果您的原生源文件还没有 CMake 构建脚本,则需要自行创建一个并包含适当的 CMake 命令。CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeList.txt
。
本部分介绍了您会用到的一些基本的 CMake 命令,用于指示在创建原生库时需要用到哪些源文件。
当您创建了一个 CMake 构建脚本后,您需要配置 Gradle 文件去关联 CMake 脚本,使 Gradle 构建的时候能将原生库打包到您的 APK 里。
注:如果您的项目使用 ndk-build,则不需要创建 CMake 构建脚本。可以通过一个指向您原生库的 Android.mk 文件,将原生库包含到 Gradle 中。
创建 CMake 脚本
要创建一个用作 CMake 构建脚本的纯文本文件,请按照以下步骤操作:
- IDE 切换到 Project 视图。
- 右键点击 您的模块 的根目录,选择 New > File。
注:您可以在任何位置创建脚本文件,不过在构建脚本文件时,原生源文件和库的路径将和构建脚本的位置相关。
- 输入文件名 “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()
命令来关联 log 库。
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 分析器验证哪些库打包到您的 APK 中。
关联其他 CMake 项目
如果您想编译多个 CMake 项目并将其添加到您的 Android 工程中,您可以让 Gradle 只关联一个 CMakeLists.txt
脚本文件作为一级脚本文件,在这个脚本文件中添加其它 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 )