目录
CMake及NDK简介
CMake是一个开源的跨平台自动化建构工具,它使用CMakeLists.txt来描述构建过程,可以产生标准的构建文件。Android平台上使用 CMake进行动态库(C和C++代码)的编译和使用。
NDK(native development kit)是一种基于原生程序接口的软件开发工具。通过此工具开发的程序直接以本地语言运行,而非虚拟机。因此只有java等基于虚拟机运行的语言的程序才会有原生开发工具包。Android中的NDK提供了一系列的工具,帮助开发者快速开发C或C++的动态库,并能自动将so和java应用一起打包成apk。
NDK常使用于:对某些代码进行保护(由于Java代码容易被反编译,而C/C++不容易);为了便于平台的移植;在程序中常常需要调用一些第三方的C/C++库;位了提升某些情况下代码的性能;提高利用某些现存的代码库文件。
- 交叉编译?就是在一个平台上生成另外一个平台的可执行代码。
- JNI是什么?JNI是Java Native Interface的缩写,即Java本地接口,JNI是Java调用Native语言的一种特性。通过JNI可以使得Java与C/C++机型交互。
- 链接库?静态链接库:在编译的链接阶段将所有依赖的代码嵌入到应用程序的内部成为一个文件,运行时能在任何地方执行而不需要单独查找依赖资源,代码体积大。动态链接库:只将地址信息告诉了链接器,运行时才查找依赖资源,不能单独运行,代码体积小。
1. CMake使用
NDK通过工具链文件支持CMake。工具链文件是用于自定义交叉编译工具链行为的CMake文件。用于NDK的工具链文件位于NDK中的 /build/cmake/android.toolchain.cmake。如果安装了Android SDK,则NDK安装在的SDK目录的ndk/version/或ndk-bundle/中。
- 警告1:CMake 拥有自身的内置 NDK 支持。此工作流程不受 Android 支持,并且在使用新的 NDK 版本或使用旧版本的 CMake 时经常损坏。强烈建议不要使用此工作流程,它也不能与 Gradle 配合使用。
Android Gradle 插件会将用于为每个 ABI 和编译类型对执行 CMake 编译的编译参数保存至 cmake_build_command.txt。这些文件位于:<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
。较旧版本的 Android Gradle 插件会将这些文件放入 .externalNativeBuild 目录而不是 .cxx 目录。
1.1 创建NDK项目
- 安装JDK1.8,Android studio3.6.1(2.3版本以后开始支持CMake编译),默认采用的gradle版本为5.6.4。
- 在Android studio中下载NDK和CMake,也可在官网:https://developer.android.google.cn/ndk/downloads/下载之后在Android studio中设置其路径。
- 警告2:NDK 库无法在 API 级别低于编译代码所用的 ANDROID_PLATFORM 值的设备上运行。
- 创建项目如同所示:
- 通过native打印字符串
与普通Android项目不同的是,CMake编译的项目在src下面增加了一个cpp目录,在app\build.gradle中增加externalNativeBuild。即把普通工程添加CMake编译选项只需要修改这些地方。得到目录结构如下:
得到的CMakeLists文件:
cmake_minimum_required(VERSION 3.4.1)
# 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.
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).
native-lib.cpp )
# 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.
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 )
# 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.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
native-lib.cpp代码如下:
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
在Android机器上运行之后屏幕将会打印:Hello from C++
1.2 常用语法
# 打印变量
set(var test)
message(${
test)
# 打印cmake及文件夹路径
message(${CMAKE_CURRENT_LIST_FILE})
MESSAGE(${CMAKE_CURRENT_LIST_DIR})
# 逻辑判断
IF(TRUE)
MESSAGE("This is TRUE")
ENDIF()
# 循环
FOREACH(lib ${LIBRARY_OUTPUT_PATH})
MESSAGE(${lib})
ENDFOREACH()
# 文件操作
FILE(APPEND file_name "123")
FILE(READ file_name test)
FILE(WRITE file_name "321")
1.3 cmake编译动态库
这里简单实现动态库之间的关联示例,新建一个C++ class,在CMakeLists中通过add_library把C++文件变成库,再通过target_link_libraries进行关联
测试代码如下:
// test.h文件
#ifndef MY_APPLICATION_TEST_H
#