NDK-CMake初探

/ 0评 / 0

前言

随着对Android学习的越来越深入,开始接触到NDK相关知识了,想当年还为了以后没机会使用C/C++而心伤,现在终于有机会在Android上使用C/C++了,下面就来介绍一下Android Studio2.2以后带来的新特性CMake。

官方文档

关于在Android Studio2.2及以后版本上面使用CMake构建NDK项目,可以查看官方文档:传送门。里面详细介绍了CMake的安装以及使用,本篇博客也是基于此文档进行进一步说明。关于NDK环境的下载以及Demo工程的创建可以查看贴出的官方地址》》》传送门!!!

运行Demo

按照官方文档中的新建一个NDK工程,目录结构如下:

1.对应C/C++源文件(*.c、*.cpp、*h)

2.CMakeLists.txt文件为主要配置文件,具体可以查看下文的介绍。

生成SO文件。

点击状态栏上面的Bulid->Rebuild Project,生成的so在\app\build\intermediates\cmake\debug\obj\路径下。如下图所示,默认生成支持全部平台的so。不过一般来说,我们仅仅需要arm64-v8a、armeabi、armeabi-v7a甚至只需要armeabi即可。

CMakeLists.txt文件的介绍

CMakeLists.txt类似于我们以前使用ndk-build时使用的Android.mk,主要是用来配置C/C++文件的依赖关系以及生成so等,下面就以官方的Demo来做介绍。具体请查看下面贴出的中文注释。

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

#创建一个名字为native-lib的动态链接库(实际生成为libnative-lib.so)
#你可以指定为STATIC(*.a)或者SHARED(*.so),可以通过这个生成多个动态链接库
#通过第三个参数指定源码路径,多个源码使用空格隔开
add_library( # Sets 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 )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

# 指定另外一个动态链接库连接到指定的动态链接库,这里是将log-lib这个
# 链接到native-lib,可以指定多个,使用空格隔开
target_link_libraries( # Specifies the target library.
                       native-lib
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
			 
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before

# completing its build.
# 使用这个命令可以导入NDK本地的链接库
# 下面这个是导入本地的log链接库并命名为log-lib,通过${log-lib}引用
find_library( # Sets the name of the path variable.
              log-lib
              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

add_library命令

命令说明:

add_library可以引入源文件(*.c、*.cpp)并将其编译为so文件,也可以导入已经编译好的so到项目中。

基本用法

add_library( #指定生成so的名字(libnative-lib)
             native-lib
             #指定生成为SHARED版本(*.so)
             SHARED
			 #制定源文件位置,多个使用空格隔开
             src/main/cpp/native-lib.cpp )

add_library一共有三个参数,第一个为库名称,最终的so文件名为lib库名称.so,不过最后使用的时候只需要使用库名称即可。

比如在构建脚本中指定“native-lib”作为共享库的名称,CMake 将创建一个名称为 lib<em><span style="color: #0000ff;">native-lib</span></em>.so 的文件。不过,在 Java 代码中加载此库时,只需要加入如下代码。

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

第二个参数为库的版本,有SHARED以及STATIC可选,SHARED生成动态链接库(*.so),STATIC生成静态链接库(*.a)

第三个参数为编译库所需要的源文件位置,如果有多个源文件,那么使用空格或者回车隔开即可。

如果工程中有多个源文件需要编为不同的so,那么在CMakeLists.txt中使用多个add_library,并且分别指定好源文件以及生成的名字即可。Demo如下。

将使用native-lib.cpp编译出libnative-lib.so,使用native-lib2333.cpp编译出libnative-lib233.so

add_library(native-lib
			SHARED
			src/main/cpp/native-lib.cpp)
add_library(native-lib233
			SHARED
			src/main/cpp/native-lib2333.cpp)

高级用法:

如果我们需要导入已经存在的so文件到项目中,那么在使用add_library的同时也需要使用set_target_properties指定so文件的位置,如果so文件有头文件,那么还需要使用include_directories指定头文件的位置,Demo如下,具体介绍请查看中文注释。

add_library(#需要导入libimported-lib.so
			imported-lib
			#指定为SHARED模式
			SHARED
			#指定为外部导入的共享库
			IMPORTED )
			
set_target_properties( #这里是上面指定的名字
                       imported-lib

                       #这一行默认这样写
                       PROPERTIES IMPORTED_LOCATION

                       #指定so文件存在的位置
					   #app/src/main/jniLibs/cpu平台/
					   ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libimported-lib.so)
					   
# 如果so文件需要头文件,通过这个命令引入头文件
# 头文件位置/app/src/main/cpp/imported-lib/include/
include_directories(${PROJECT_SOURCE_DIR}/src/main/cpp/imported-lib/include)

find_library与target_link_libraries

命令说明:

预构建的 NDK 库已经存在于 Android 平台上,因此,我们无需再构建或将其打包到 APK 中。由于 NDK 库已经是 CMake 搜索路径的一部分,所以我们不需要在本地 NDK 安装中指定库的位置 - 只需要在 CMake 中设置希望使用的库的名称,并通过target_link_libraries命令将其关联到我们自己的原生库。

基本用法

find_library( #指定本地链接库的引用名,通过${log-lib}引用
              log-lib
              #指定NDK本地链接库的名字
              log )

# 将上面指定的log库链接到libnative-lib.so中,也可以链接多个
# 通过空格隔开即可
target_link_libraries( native-lib  ${log-lib} )

Cmake添加编译参数

在我们使用Cmake编译so的时候,默认使用的Android平台版本为minSdkVersion所指定的版本,如果我们想指定其他的版本,那么就需要添加一些配置参数。语法如下,参数名使用-D开头,多个参数使用,号隔开。

arguments "-DVAR_NAME=ARGUMENT","-DVAR_NAME=ARGUMENT"

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake build script.
    externalNativeBuild {
      cmake {
        ...
        // Use the following syntax when passing arguments to variables:
        // arguments "-DVAR_NAME=ARGUMENT".
        arguments "-DANDROID_ARM_NEON=TRUE",
        // If you're passing multiple arguments to a variable, pass them together:
        // arguments "-DVAR_NAME=ARG_1 ARG_2"
        // The following line passes 'rtti' and 'exceptions' to 'ANDROID_CPP_FEATURES'.
                  "-DANDROID_CPP_FEATURES=rtti exceptions"
      }
    }
  }
  buildTypes {...}

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

举例:设置CMake编译版本为android-21

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_PLATFORM=android-21"
            }
        }
    }
}

更多可选参数请查看文末官方文档(需科学上网),或者你也可以看看这篇博客

其他

设置so文件生成位置

在上面我们说过了,so默认生成位置为\app\build\intermediates\cmake\debug\obj\(<project-root>/<module-root>/.externalNativeBuild/cmake/<build-type>/<ABI>/),不过我们一般将so放在/src/main/jniLibs/下面,这一点我们也可以在CMakeLists.txt文件中进行设置。

#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})

设置SO文件支持的cpu类型

CMake默认生成全部cpu架构支持的so文件,但是就像上面所说的,我们一般不需要这么多的,在app/下的build.gradle文件中,配置一下即可,下面的例子是只生成armeabi架构的CPU。

#其他的省略
android {
	.....
    defaultConfig {
		.....
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'armeabi'
            }
        }
    }
	.....
}

官方文档:https://developer.android.com/ndk/guides/cmake.html

发表评论

您的电子邮箱地址不会被公开。