NDK (Native development kit): 官方文档
原生开发套件 (NDK) 是一套工具,使您能够在 Android 应用中使用 C 和 C++ 代码,并提供众多平台库,您可使用这些平台库管理原生 activity 和访问实体设备组件,例如传感器和触控输入。NDK 可能不适合大多数 Android 编程初学者,这些初学者只需使用 Java 代码和框架 API 开发应用。然而,如果您需要实现以下一个或多个目标,那么 NDK 就能派上用场:
- 进一步提升设备性能,以降低延迟或运行游戏或物理模拟等计算密集型应用。
- 使用您自己或其他开发者的 C 或 C++ 库。
您可以在 Android Studio 2.2 或更高版本中使用 NDK 将 C 和 C++ 代码编译到原生库中,然后使用 Android Studio 的集成构建系统 Gradle 将原生库打包到 APK 中。Java 代码随后可以通过 Java 原生接口 (JNI) 框架调用原生库中的函数。如需详细了解 Gradle 和 Android 构建系统,请参阅配置您的版本。
1. 新建项目
可选两种新建项目方式
第一种:选择NativeC++新建
第二种:添加C++Module
在已有的项目里选择要使用ndk开发的module,右键......
2. 跑起来
如果是第一种新建方式,那就直接运行即可
Tips: 不同的 Android Studio 版本可能会导致不同的结果,如果运行失败,可参考 3. 分析 来排查错误
如果是第二种,则需要稍微写点东西,你肯定听说过 Java
的 native
方法,在 Kotlin
里我们使用 external
这个关键字
我们要写的就是 external (native)
方法,以及加载 native lib
的代码,因为编译器总要知道你希望什么时候开始把 native lib
里面的东西,加载到内存里。
So,在 MainActivity 里添加
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val tv = findViewById<TextView>(R.id.hello)
tv.text = hello()
}
// 这里时加载你自己所需的 native lib
// 如果是 C、C++ 代码,应该可以在 CMakeLists.txt 找到库的名字
companion object {
init {
System.loadLibrary("my_ndk_lib")
}
}
// external 方法 (jni 中间层)
external fun hello(): String
}
关于 System.loadLibrary("my_ndk_lib")
在 cpp 文件夹下里有一个自动生成的 .cpp 文件,应该是和你项目名一样,下面介绍 CMakeLists.txt 的时候会提到
在里面写
extern "C"
JNIEXPORT jstring JNICALL
Java_com_czb_ndk_1jni_1kotlin_MainActivity_hello(JNIEnv *env, jobject thiz) {
std::string s = "Hello";
jstring js = env->NewStringUTF(s.c_str());
return js;
}
Java
_ com_czb_ndk_1jni_1kotlin
_ MainActivity
_ hello
,你应该已经观察到了,Java代表语言,后面跟的是包名,再后面是类名,最后是方法名
Tips:方法名一定要和包名对应,我不建议手敲,因为这里要用
_
来代替包名的.
如果包名中含有
_
则用_1
来代替,如果包名里有_1
,则用_11
来代替......
可以直接在 MainActivity 里,external fun hello(): String
这里点击 hello ,然后option + enter (alt + 回车),直接生成然后将方法体内容填写即可
Tips:
jni
代码(extrnal、native
方法)里的方法没有实现(没有大括号{}),只有声明,具体实现在 C、C++ 代码里
3. 分析
ndk开发时 代码跑起来需要这几个东西
- cpp 文件夹以及内部的 CMakeLists.txt cpp 文件或者.so等编好的库文件 作为 native lib
- jni 代码来作为 Java、Koltin 等基于 Jvm 语言调用的中间层
- 将中间层,native lib,Java 代码 连接起来的配置文件,比如 build.gradle
- 加载库的必要代码,比如
System.loadLibrary("my_ndk_lib")
build.gradle 怎么连接 native lib
最新的 Android Studio 已经可以在你 添加C++ Module 的时候,直接帮你配置好build.gradle
如果你要自己写
如需手动配置 Gradle 以关联到您的原生库,您需要将 externalNativeBuild
块添加到模块里的 build.gradle
文件中,并使用 cmake
或 ndkBuild
块对其进行配置:
Groovy 版本
android {
...
defaultConfig {...}
buildTypes {...}
externalNativeBuild {
cmake {
// CMakeLists.txt的地址
path "CMakeLists.txt"
}
}
}
Kotlin 版本
android {
...
defaultConfig {...}
buildTypes {...}
externalNativeBuild {
cmake {
// CMakeLists.txt的地址
path = file("CMakeLists.txt")
}
}
}
注意:如果您要将 Gradle 关联到现有的 ndk-build 项目,请使用
ndkBuild
块(而不是cmake
块),并提供指向Android.mk
文件的相对路径。如果Application.mk
文件与您的Android.mk
文件位于同一目录下,Gradle 也会包含此文件。
CMakeLists.txt怎么写
一般写这5个就行
- cmake_minimum_required 这个是 cmake 要求的最低版本
- project 这个是项目名
- add_library 这个是添加 lib
- find_library 这个是搜索并添加 lib
- target_link_libraries 这个是连接 lib
# 有关将 CMake 与 Android Studio 配合使用的更多信息,
# 请阅读文档:https:d.android.comstudioprojectsadd-native-code.html
# 设置构建本机库所需的最低 CMake 版本。
cmake_minimum_required(VERSION 3.18.1)
# 声明并命名项目。
project("ndk_jni_kotlin")
# 创建并命名库,将其设置为 STATIC 或 SHARED,并提供其源代码的相对路径。
# 您可以定义多个库,CMake 会为您构建它们。Gradle 会自动将共享库与您的 APK 打包。
add_library( # 设置库的名称。
my_ndk_lib
# 将库设置为共享库。
SHARED
# 提供源文件的相对路径。
my_ndk_lib.cpp)
# 搜索指定的预生成库并将路径存储为变量。
# 由于 CMake 默认在搜索路径中包含系统库,
# 因此您只需指定要添加的公有 NDK 库的名称。
# CMake 会在完成构建之前验证库是否存在。
find_library( # 设置路径变量的名称。
log-lib
# 指定您希望 CMake 查找的 NDK 库的名称。
log)
# 指定 CMake 应链接到目标库的库。可以链接多个库,
# 例如在此生成脚本中定义的库、预生成的第三方库或系统库。
target_link_libraries( # 指定目标库。
my_ndk_lib
# 将目标库链接到 NDK 中包含的日志库。
${log-lib})
您可以使用 set_target_properties()
命令指定库的路径,具体命令如下所示。
某些库会针对特定的 CPU 架构或应用二进制接口 (ABI) 提供单独的软件包,并将其整理到单独的目录中。此方法既有助于库充分利用特定的 CPU 架构,又能让您只使用所需的库版本。如需向 CMake 构建脚本添加库的多个 ABI 版本,而不必为库的每个版本编写多个命令,您可以使用 ANDROID_ABI
路径变量。此变量使用的是 NDK 支持的一组默认 ABI,或者您手动配置 Gradle 而让其使用的一组经过过滤的 ABI。
为了让 CMake 能够在编译时找到头文件,您可以使用 include_directories()
命令并包含相应头文件的路径
一开始代码跑不起来的原因:
- cpp 方法名没写对
- cpp 方法实现没写对
- 忘记写
System.loadLibrary("my_ndk_lib")