1、CMake工具
1.1、CMake是什么
- CMake是一个主要用于CPP的构建工具。
- CMake语言是平台无关的中间编译工具。同一个CMake编译规则在不同系统平台构建出不同的可执行构建文件,所有操作都是通过编译CMakeLists.txt来完成的。在Linux产生MakeFile,在Windows平台产生Visual Studio工程等。
- CMake旨在解决各平台的不同Make工具的产生的差异(比如GNU Make, QT的qmake,微软的nmake, BSD的pmake)。
1.2、CMake原理
CMake有两个阶段:配置、生成。
1.3、CMake常用命令介绍
(1)cmake_minimum_required
- 用于设定需要的最低版本的CMake
cmake_minimum_required
(
VERSION <min>[...<policy_max>] [FATAL_ERROR]
)
(2)project
- 用于指定cmake工程的名称,并将其存储在变量PROJECT_NAME中
project
(
<PROJECT-NAME>
[<language-name>...]
)
或
project
(
<PROJECT-NAME>
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[DESCRIPTION <project-description-string>]
[HOMEPAGE_URL <url-string>]
[LANGUAGES <language-name>...]
)
VERSION | cmake工程的版本号 |
DESCRIPTION | cmake工程的简短的描述 |
HOMEPAGE_URL | cmake工程的主页URL |
LANGUAGES | cmake工程的编译工程使用的语言 |
(3)add_library
使用该命令可以在:
- Linux下生成(静态/动态)库so或者.a文件
- Windows下就是dll与lib文件
它有两种命令格式
-
第一种
add_library
(
<name>
[STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2] [...]
)
<name> | 库文件的名字,该库文件会根据命令里列出的源文件来创建 |
[STATIC | SHARED | MODULE] | 作用是指定生成的库文件的类型
注:如果没有明确指定要生成的library的类型到底是STATIC,SHARED还是MODULE。则查看BUILD_SHARED_LIBS变量,如果值为ON,则默认是SHARED,否则默认STATIC |
[EXCLUDE_FROM_ALL] | 表明该target是否从默认构建target中排除 |
[source1] [source2] [...] | source1 source2分别表示各个源文件 |
-
第二种
生成一个obj文件对象,该对象库只编译源文件,但不链接
add_library
(
<name> OBJECT [<source>...]
)
(4)find_library
- 该命令用来查找一个库文件
find_library
(
<VAR> name1 [path1 path2 ...]
)
<VAR> |
|
NAMES | 在NAMES参数后列出的文件名是要被搜索的库名 |
PATHS | 附加的搜索位置在PATHS参数后指定 |
(5)target_link_libraries
- 该指令的作用为将目标文件与库文件进行链接
target_link_libraries
(
<target>
[item1] [item2] [...][[debug|optimized|general] <item>] ...
)
- 上述指令中的是指通过add_executable()和add_library()指令生成已经创建的目标文件。而[item]表示库文件没有后缀的名字。
- 默认情况下,库依赖项是传递的。当这个目标链接到另一个目标时,链接到这个目标的库也会出现在另一个目标的连接线上。这个传递的接口存储在interface_link_libraries的目标属性中,可以通过设置该属性直接重写传递接口。
2、Android Studio生成.so
- Android Studio版本:Android Studio Flamingo | 2022.2.1 Patch 2
- 开发语言:Kotlin
2.1、新建C++项目
- 可以选择C++版本,我选择的是默认
新建项目中会有CMakeLists.txt和native-lib.cpp两个文件
- CMakeLists.txt:CMake工具会根据其中的命令编译生成.so文件
- native-lib.cpp:C++文件
CMakeLists.txt中的内容
# 指定需要的 CMake 最低版本为 3.22.1
cmake_minimum_required(VERSION 3.22.1)
# 指定项目名称为 "mysocreatefile"
project("mysocreatefile")
# 添加一个名为 "mysocreatefile" 的共享库,并将 "native-lib.cpp" 源文件添加到库中
# 这里使用 SHARED 标志表示这是一个共享库(动态链接库)
add_library(
mysocreatefile
SHARED
native-lib.cpp
)
# 查找名为 "log" 的库,并将其路径保存在变量 log-lib 中
# 这是为了在后面的步骤中将这个库链接到目标库
find_library(
log-lib
log
)
# 将目标库 "mysocreatefile" 与 log-lib 变量中的库链接起来。
# 这意味着在编译和链接过程中,将使用 "log" 库提供的功能和符号
target_link_libraries(
mysocreatefile
${log-lib}
)
native-lib.cpp中的内容,即C++要执行的代码
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_leon_mysocreatefile_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
MainActivity中的内容
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.sampleText.text = stringFromJNI()
}
external fun stringFromJNI(): String
companion object {
init {
System.loadLibrary("mysocreatefile")
}
}
}
- 可以看到程序已经为我们自动生成了一个动态生成.so文件的示例
- 其中生成的.so文件的库的名称叫做"mysocreatefile"
- 库中的C++方法Java_com_leon_mysocreatefile_MainActivity_stringFromJNI(),其中com_leon_mysocreatefile对应的是包名,MainActivity_stringFromJNI()对应的是类名和方法名,在Java中调用该方法时,必须使包名类名方法名与此处的保持一致,下面会示例讲解。
- 想要在MainActivity中调用C++方法,需要先加载库System.loadLibrary("mysocreatefile"),然后声明native方法stringFromJNI(),这个方法名要与C++中的方法名对应
- external对应Java中的public static native
build.gradle(:app)中,android{}目录中会有cmake的配置
我们可以Make一下项目,看一下示例生成的.so
2.2、生成自定义功能的.so文件
- 比如,我们想在.so中实现一个分数评定的功能,大于90分优秀,大于70分良好,小于60分不及格。
(1)创建一个Kotlin类
定义一个external方法
(2)创建cpp文件,用C++实现功能
- 注:C++中方法名中的包名前缀、类名前缀、方法名前缀要与定义的Kotlin类的一致。
- 如,Kotlin类的包名是com.leon.score,类名是ScoreTool,方法名是evaluateLevel
- 则C++中的方法名就是Java_com_leon_score_ScoreTool_evaluateLevel
(3)修改CMakeList.txt
- 可以将库名换一个myscorelib
- 替换cpp文件
# 指定需要的 CMake 最低版本为 3.22.1
cmake_minimum_required(VERSION 3.22.1)
# 指定项目名称为 "myscorelib"
project("myscorelib")
# 添加一个名为 "myscorelib" 的共享库,并将 "score-lib.cpp" 源文件添加到库中
# 这里使用 SHARED 标志表示这是一个共享库(动态链接库)
add_library(
myscorelib
SHARED
score-lib.cpp
)
# 查找名为 "log" 的库,并将其路径保存在变量 log-lib 中
# 这是为了在后面的步骤中将这个库链接到目标库
find_library(
log-lib
log
)
# 将目标库 "myscorelib" 与 log-lib 变量中的库链接起来。
# 这意味着在编译和链接过程中,将使用 "log" 库提供的功能和符号
target_link_libraries(
myscorelib
${log-lib}
)
(4).so文件加载和使用
- 需要通过System.loadLibrary来加载库myscorelib
package com.leon.score
class ScoreTool {
companion object{
init {
System.loadLibrary("myscorelib")
}
}
external fun evaluateLevel(score: Int): String
}
- Make Project以生成.so文件(把之前.so的缓存全部删除再make)
- 在MainActivity中调用该方法
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
//评定分数等级
val score = 50
val level = ScoreTool().evaluateLevel(score)
//展示结果
binding.sampleText.text = "得分:$score\n等级:$level"
}
}
3、在其他项目中使用自定义的.so文件
- 将cmake目录下动态生成的.so文件复制一份备用
3.1、新建一个Android项目
- 将刚才复制的.so文件放置app下的libs中
- 配置build.gradle(:app)中的sourceSets和ndk
android {
...
sourceSets{
main{
jniLibs.srcDirs = ['libs']
}
}
defaultConfig {
...
ndk {
// 设置支持的SO库架构
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
}
}
}
3.2、适配工具类
- 创建的工具类必须和.so文件中方法的包名,类名,方法名一致,才能引用到.so库中的C++方法
3.3、使用.so方法
OOM