NDK(Native Development Kit)— 是一个用C和C++编写Android应用程序部分的工具包。与普通的Android SDK不同,NDK将代码编译为本机库(.so),直接在处理器上运行,无需虚拟机层。根据Google NDK Guides, 2026,NDK用于高性能计算、游戏、音频处理和重用现有的C/C++项目。JNI(Java Native Interface)将本机代码与Kotlin和Java连接起来。
要点
NDK(Native Development Kit) — 是一个用于将C和C++代码交叉编译为Android可执行库的工具集。NDK包括Clang编译器、libc++标准库、Android API头文件和构建工具。开发人员用C或C++编写本机代码,将其编译为.so文件(共享对象),并通过JNI连接到应用程序。
NDK不会取代Android SDK,而是对其进行补充。整个UI、生命周期和系统服务仍使用Kotlin或Java。本机代码解决特定任务:数学计算、加密、编解码器、游戏中的物理效果。Google建议仅在SDK无法提供所需性能或硬件功能访问时才使用NDK。
NDK的历史始于2009年(NDK r1)。在此期间,这些工具从实验性脚本集发展成为支持CMake、LLDB调试和分析的成熟系统。截至2026年,当前版本为NDK r27,包含Clang 19、对C++20的完整支持以及libc++作为唯一的标准库。
NDK通过本机API为开发人员提供对Android底层功能的访问。主要使用场景包括:使用OpenGL ES和Vulkan进行3D图形处理、通过Oboe进行音频处理、通过BoringSSL进行加密操作以及针对ARM的NEON优化。所有这些API都可以从C/C++访问,并且需要NDK进行编译。
此外,NDK允许重用现有的C/C++库,而无需重写为Kotlin。这对于具有多年C++历史的项目尤其重要——游戏引擎(Unity、Unreal Engine)、计算机视觉库(OpenCV)或加密包(OpenSSL)。在这种情况下,NDK可以节省多年的开发时间。
| 组件 | 用途 |
|---|---|
| Clang | 支持C++20的C/C++编译器 |
| libc++ | 标准C++库(NDK r27中唯一) |
| CMake | 本机库的构建系统 |
| LLDB | Android Studio中的本机代码调试器 |
| ndk-build | 过时的构建系统(已被CMake替代) |
在NDK和纯SDK之间的选择取决于任务。Android SDK为90%的场景提供现成的Java/Kotlin API——网络、文件、通知、相机操作。当这些API无法提供所需的性能或功能时,就会连接NDK。例如,通过SDK进行实时音频处理是可能的,但延迟为50-200毫秒,而通过Oboe的本机库将延迟降低到5-15毫秒。
APK大小——另一个因素。添加NDK会增加应用程序的大小,因为每个ABI架构都需要单独的.so库。但是,使用Android App Bundle(AAB)可以缓解这个问题:Google Play只向用户提供其架构的库。一个典型的本机项目会在设备上增加1-10 MB的安装大小。
| 标准 | SDK(Kotlin/Java) | NDK(C/C++) |
|---|---|---|
| 性能 | 中等(JIT/AOT编译) | 高(机器码) |
| 音频延迟 | 50-200毫秒 | 通过Oboe为5-15毫秒 |
| 3D图形 | 通过Canvas/OpenGL Kotlin API | 直接Vulkan/OpenGL |
| 开发复杂度 | 低 | 高(内存管理,JNI) |
| 代码可移植性 | 仅限Android | Linux、Windows、macOS、iOS |
| APK大小 | 最小 | 每个库+1-10 MB |
NDK通过SDK Manager与Android SDK分开安装。在NDK目录中包含工具链(编译器)、平台库、头文件和工具。NDK工具链包括用于针对所有目标ABI进行交叉编译的Clang:arm64-v8a、armeabi-v7a、x86_64和x86。编译器根据CMake或ndk-build标志自动选择正确的架构。
用于本机代码的Android API由sysroot/usr/include目录中的头文件表示。那里包含了本机代码中所有可用API的声明:用于Activity生命周期的native_activity.h、用于输入事件的input.h、用于传感器的sensor.h。连接这些头文件无需JNI层即可访问硬件功能。
ndk/
toolchains/
llvm/prebuilt/windows-x86_64/
bin/ // Clang, ld, llvm-profdata
sysroot/ // Android API头文件
platforms/
android-26/ // 适用于API 26的libc、libm、libdl
android-34/
sources/
cxx-stl/ // libc++头文件
build/
cmake/ // CMake工具链文件
通过NDK,可以使用以下本机API组:Native App Glue(从C语言管理Activity生命周期)、OpenGL ES 3.2和Vulkan 1.3(图形)、Oboe(低延迟音频)、Neural Networks API(设备上的机器学习)。每个API在NDK组件中都有头文件和静态/动态库。
使用本机API不需要JNI——函数直接从C/C++代码调用。然而,与Kotlin中的UI交互仍然需要JNI。这种混合架构出现在游戏引擎中:图形和物理用C++(通过Vulkan),菜单和UI用Kotlin(通过Jetpack Compose)。
JNI(Java Native Interface) — 从Java/Kotlin虚拟机调用本机代码的标准机制。开发人员在Kotlin中使用external关键字声明外部函数,并通过System.loadLibrary连接.so库。在C++端,函数使用JNI命名约定声明,该约定对包和类名进行编码。
class NativeBridge {
companion object {
init {
System.loadLibrary("native-lib")
}
}
external fun stringFromJNI(): String
external fun fibonacci(n: Int): Long
external fun processBuffer(data: ByteArray): ByteArray
}
// 在代码中使用
val bridge = NativeBridge()
println(bridge.stringFromJNI()) // "Hello from C++"
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_app_NativeBridge_stringFromJNI(
JNIEnv* env, jobject /* this */) {
std::string hello = "Hello from C++ NDK";
return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_example_app_NativeBridge_fibonacci(
JNIEnv* env, jobject /* this */, jint n) {
if (n <= 1) return n;
jlong a = 0, b = 1;
for (int i = 2; i <= n; i++) {
jlong temp = a + b;
a = b;
b = temp;
}
return b;
}
CMake — NDK的主要构建系统,取代了过时的ndk-build。CMakeLists.txt文件描述源文件、库和编译标志。如果在build.gradle中配置了externalNativeBuild,Android Studio会在构建项目时自动调用CMake。CMake为每个目标ABI生成make文件,并并行编译本机代码。
cmake_minimum_required(VERSION 3.22.1)
project("nativelib")
# 连接头文件
include_directories(src/main/cpp/include)
# 创建本机库
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp
src/main/cpp/math_utils.cpp
src/main/cpp/audio_processor.cpp
)
# 连接Android系统库
target_link_libraries(
native-lib
android
log
OpenSLES
# libc++自动连接
)
# 优化标志
target_compile_options(native-lib PRIVATE -O3 -Wall -Wextra)
android {
defaultConfig {
ndk {
// 构建的目标ABI
abiFilters "arm64-v8a", "armeabi-v7a", "x86_64"
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
version "3.22.1"
}
}
buildTypes {
release {
externalNativeBuild {
cmake {
arguments "-DCMAKE_BUILD_TYPE=Release"
}
}
}
}
}
ABI(Application Binary Interface) — 机器码的格式,决定与设备处理器的兼容性。每个ARM或x86架构都有自己的ABI。NDK针对每个指定的ABI分别编译本机代码。2026年最常见的ABI:arm64-v8a(99%的现代设备)、armeabi-v7a(旧的32位设备)、x86_64(模拟器和Chromebook)。
在build.gradle中指定abiFilters将构建限制为仅必要的架构,从而缩短编译时间。对于Google Play,建议包含库兼容的所有ABI——这保证了在所有设备上的运行。Google Play Console允许通过App Bundle配置特定ABI的APK交付。
#include <jni.h>
#include <android/api-level.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_app_NativeBridge_getABIInfo(
JNIEnv* env, jobject /* this */) {
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
return env->NewStringUTF("armeabi-v7a");
#endif
#elif defined(__aarch64__)
return env->NewStringUTF("arm64-v8a");
#elif defined(__x86_64__)
return env->NewStringUTF("x86_64");
#elif defined(__i386__)
return env->NewStringUTF("x86");
#endif
return env->NewStringUTF("unknown");
}
| ABI | 架构 | 位数 | 设备 |
|---|---|---|---|
| arm64-v8a | ARMv8-A | 64位 | 几乎所有现代手机 |
| armeabi-v7a | ARMv7-A | 32位 | 旧设备(2020年之前) |
| x86_64 | x86-64 | 64位 | 模拟器、Chromebook |
| x86 | x86 IA-32 | 32位 | 过时的模拟器 |
让我们看一个实际示例:用于处理浮点数的数学库。由于直接访问NEON ARM指令和运行时没有数组边界检查,C++中的本机代码比Kotlin更高效地执行计算。此示例演示了NDK使用的典型模式——将繁重计算转移到本机层。
#include <jni.h>
#include <cmath>
#include <vector>
extern "C" JNIEXPORT jfloatArray JNICALL
Java_com_example_app_NativeBridge_normalizeArray(
JNIEnv* env, jobject, jfloatArray input) {
jsize len = env->GetArrayLength(input);
jfloat* elements = env->GetFloatArrayElements(input, nullptr);
// 计算平均值和标准差
float sum = 0.0f, sumSq = 0.0f;
for (jsize i = 0; i < len; i++) {
sum += elements[i];
sumSq += elements[i] * elements[i];
}
float mean = sum / len;
float stddev = std::sqrt(sumSq / len - mean * mean);
// 归一化:(x - mean) / stddev
jfloat* result = new jfloat[len];
for (jsize i = 0; i < len; i++) {
result[i] = (elements[i] - mean) / stddev;
}
env->ReleaseFloatArrayElements(input, elements, JNI_ABORT);
jfloatArray output = env->NewFloatArray(len);
env->SetFloatArrayRegion(output, 0, len, result);
delete[] result;
return output;
}
要调试本机代码,请使用来自android/log.h库的__android_log_print宏。消息会出现在Logcat中,与Java/Kotlin日志一起显示。日志级别(ANDROID_LOG_DEBUG、ANDROID_LOG_ERROR)有助于过滤消息。
#include <android/log.h>
#define LOG_TAG "NativeLib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
extern "C" JNIEXPORT void JNICALL
Java_com_example_app_NativeBridge_processData(
JNIEnv* env, jobject, jint count) {
LOGD("Processing %d items", count);
for (int i = 0; i < count; i++) {
// 繁重处理
LOGD("Item %d processed", i);
}
LOGD("Processing complete");
}
常见问题
不。对于大多数应用程序,使用Kotlin的SDK就足够了。需要NDK用于高性能任务:实时音频/视频处理、3D图形、加密或重用现有的C/C++项目。
NDK使用来自LLVM工具链的Clang。从NDK r23开始,GCC已被完全移除。Clang为所有Android ABI交叉编译代码:arm64-v8a、armeabi-v7a、x86_64、x86。
ABI(Application Binary Interface)——机器码的格式,决定与处理器的兼容性。NDK为每个ABI分别构建.so库。arm64-v8a是现代Android设备的主要ABI。
可以。Android Studio支持LLDB——本机代码调试器。可以在C++文件中设置断点,查看变量和调用堆栈。需要NDK和LLDB插件才能工作。
每个本机库会为APK增加100 KB到几MB的大小。每个ABI都需要单独的.so库。Android App Bundle只向用户提供适合的架构,从而减少安装大小。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。