1、什么是Cmake?
C/C++ 的编译文件在不同平台是不一样的。Unix 下使用 makefile 文件编译,Windows 下使用 project 文件编译。而 CMake 则是一个跨平台的编译工具,它并不会直接编译出对象,而是根据自定义的语言规则(CMakeLists.txt)生成 对应 makefile 或 project 文件,然后再调用底层的编译.
谷歌从AndroidStudio2.2以上就添加了Cmake方式来编译NDK代码,相较之前复杂的NDK-BUILDE方式,Cmake则简单很多。
2、什么是NDK?
NDK全名Native Develop Kit,andriod本地开发工具,是Google开发的一套方便开发者在Android 平台上开发Native 代码的工具;
使用NDK自带的工具,可快速对C/C++代码进行构建、编译和打包,最终生成动态/静态库供开发者使用,且不容易被反编译;
3、如何导入NDK与Cmake环境?
3.1在Setting的Android SDK中勾选以下配置;
3.2 因为Cmake要用到NDK,所以Android NDK location需要指定本地NDK路径;
4、如何在已有AS项目中导入Cmake?
4.1导入Cmake相关文件;
路径可以自定义,这里选择的是"app/src/main/cpp/",其中CMakeLists.txt是Cmake的执行脚本, test-jni.c是Jni文件,用于编译生成so库;
4.2. app工程关联Cmake
打开app目录下的build.gradle(见上图红框),添加以下语句,使AS工程关联上Cmake用于编译;
参考网址: https://www.cnblogs.com/qcjd/p/9324903.html
android{
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags ""
// Passes optional arguments to CMake.//编译选项,与makefile类似
//arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
// Sets optional flags for the C compiler.预置C的宏
//cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"
// Sets a flag to enable format macro constants for the C++ compiler.预置C++的宏
//cppFlags "-D__STDC_FORMAT_MACROS"
}
}
ndk {//编译生成哪个CPU平台的库
abiFilters 'armeabi-v7a','x86_64'
// ARMv5——armeabi
// ARMv7 ——armeabi-v7a
// ARMv8——arm64- v8a
// x86——x86
// MIPS ——mips
// MIPS64——mips64
// x86_64——x86_64
}
}
...
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt') //CMakeLists.txt存放路径
version "3.10.2"
}
}
...
}
5、Cmake常用语法介绍
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
project(NDK)
###############参考网址:https://www.cnblogs.com/qcjd/p/9324903.html
# ==========================================语法知识(start)=====================================
# 0==================<多文件编译生成库>=============================
#1、 设置多文件目录---方法1: (常用)
#1.1查找src/main/cpp目录下所有以*./c *.cpp文件并保存到DIR_SRCS变量里
file(GLOB DIR_SRCS “src/main/cpp/*.c” “src/main/cpp/*.cpp”)
#1.2不同级目录下的指定
file(GLOB DIR_SRCS “../src2/main/cpp/*.c” “../src3/main/cpp/*.cpp”)
#1.3如果cpp目录下还有别的子目录,但又不想每个子目录路径都制定过来,则可以用到递归查找
file(GLOB_RECURSE SRC_FILES "src/hello*")#注意:这里的目录不能有特殊字符,例如:<SerialPort_c>
#2、设置多文件目录---方法2:指定当前目录下的源文件,保存到<DIR_SRCS>变量中
aux_source_directory(. DIR_SRCS)
#3、关联1或2中的DIR_SRCS变量,生成库
# # SHARED: 动态库 STATIC:静态库、
add_library(hello-jni SHARED ${DIR_SRCS})
# 制定生成目标(可忽略)
add_executable(mathPowerDemo2 ${ALL_SRCS})
# ==================<引入已有预编译好的静态库/动态库>=========================
# 1.1、引入已有的预编译好的静态库
# StaticTest 可以随便起名字(原名:libStaticTest.a)
# IMPORTED: 表示静态库是以导入的形式添加进来(预编译静态库)
add_library(StaticTest STATIC IMPORTED)
# 1.2、引入已有的预编译好的动态库
# SharedTest 可以随便起名字(原名:libSharedTest.so)
# IMPORTED: 表示静态库是以导入的形式添加进来(预编译静态库)
add_library(SharedTest SHARED IMPORTED)
# 2.1、设置1.1/1.2中指定的动态库/静态库的导入路径
# 下面CMAKE_SOURCE_DIR 是系统预定义好的变量,代表当前CMakeLists.txt所在的目录
set_target_properties(StaticTest PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/static/armeabi-v7a/libStaticTest.a)
#下面${ANDROID_ABI}中的ANDROID_ABI应该指各个ABI版本的目录
# othermodule为库名称
set_target_properties(othermodule PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libothermodule.so)
# 各个ABI版本
# ARMv5——armeabi
# ARMv7 ——armeabi-v7a
# ARMv8——arm64- v8a
# x86——x86
# MIPS ——mips
# MIPS64——mips64
# x86_64——x86_64
# 2.2、设置动态库的导入路径
# 下面CMAKE_SOURCE_DIR 是系统预定义好的变量,代表当前CMakeLists.txt所在的目录
set_target_properties(StaticTest PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/static/armeabi-v7a/libSharedTest.so)
# 3.1 额外头文件查找路径设置 相当于makefile中的-I
include_directories(src/main/include)//本地或者外部库需要的头文件路径指定
# 3.2 使用其余CMakeLists.txt配置
# 额外引入src/main/subcmakelist目录的cmakelist
add_subdirectory(src/main/subcmakelist)
# 3.3 增加指定的宏定义 相当于-D
add_definitions(-D_LINUX -D_ANDROID)
# 4、配置动态库链接,生成native-lib动态库需要用到StaticTest log动态或者静态库
target_link_libraries( # Specifies the target library.
native-lib
# libTest.so 可以去掉lib与.so
StaticTest
SharedTest
log )
# 动态库这样引入没有版本差异,如果像上面那样引入会有版本问题
# CMAKE_CXX_FLAGS 会传给c++编译器
# CMAKE_C_FLAGS 会传给c编译器
# CMAKE_SOURCE_DIR 的值是当前CMakelist.txt所在目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
# ===========================================语法知识(end)========================================
# ===========================================使用范本========================================
# 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.
# 配置so库信息
add_library( # Sets the name of the library.
# 生成的so库名称,此处生成的so文件名称是libnative-lib.so
native-lib
# Sets the library as a shared library.
# STATIC:静态库,是目标文件的归档文件,在链接其它目标的时候使用
# SHARED:动态库,会被动态链接,在运行时被加载
# MODULE:模块库,是不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接
SHARED
# Provides a relative path to your source file(s).
# 资源文件,可以多个,
# 资源路径是相对路径,相对于本CMakeLists.txt所在目录
src/maind/jni/XJni.c)
# 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.
# android系统每个类型的库会存放一个特定的位置,而log库存放在log-lib中
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
# android系统在c环境下打log到logcat的库
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} )