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-файлы (shared objects) и подключает к приложению через JNI.
NDK не заменяет Android SDK, а дополняет его. Весь UI, lifecycle и системные сервисы остаются на Kotlin или Java. Нативный код решает узкие задачи: математические вычисления, криптография, кодеки, физика в играх. Google рекомендует использовать NDK только когда SDK не обеспечивает нужной производительности или доступ к аппаратным возможностям.
История NDK начинается с 2009 года (NDK r1). За это время инструментарий прошёл путь от экспериментального набора скриптов до зрелой системы с поддержкой CMake, LLDB-отладки и профилирования. На 2026 год актуальная версия — NDK r27 с Clang 19, полной поддержкой C++20 и libc++ как единственной стандартной библиотекой.
NDK даёт разработчику доступ к низкоуровневым возможностям Android через нативные API. Основные сценарии использования включают: работа с OpenGL ES и Vulkan для 3D-графики, обработка аудио через Oboe, криптографические операции через BoringSSL и NEON-оптимизации для ARM. Все эти API доступны из C/C++ и требуют NDK для компиляции.
Кроме того, NDK позволяет переиспользовать существующие C/C++ библиотеки без переписывания на Kotlin. Это особенно актуально для проектов с многолетней историей на C++ — игровых движков (Unity, Unreal Engine), библиотек компьютерного зрения (OpenCV) или криптографических пакетов (OpenSSL). В таких случаях NDK экономит годы разработки.
| Компонент | Назначение |
|---|---|
| Clang | Компилятор C/C++ с поддержкой C++20 |
| libc++ | Стандартная библиотека C++ (единственная в NDK r27) |
| CMake | Система сборки нативных библиотек |
| LLDB | Отладчик нативного кода в Android Studio |
| ndk-build | Устаревшая система сборки (заменена CMake) |
Выбор между NDK и чистым SDK зависит от задачи. Android SDK предоставляет готовые Java/Kotlin API для 90% сценариев — работа с сетью, файлами, уведомлениями, камерой. NDK подключают, когда эти API не обеспечивают нужной производительности или функциональности. Например, обработка аудио в реальном времени через SDK возможна, но с задержками 50–200 мс, в то время как нативная библиотека через Oboe снижает задержку до 5–15 мс.
Размер APK — ещё один фактор. Добавление NDK увеличивает размер приложения, так как каждая ABI-архитектура требует отдельной .so-библиотеки. Однако использование Android App Bundle (AAB) смягчает проблему: Google Play доставляет пользователю только библиотеку под его архитектуру. Типичный нативный проект добавляет 1–10 МБ к размеру установки на устройство.
| Критерий | SDK (Kotlin/Java) | NDK (C/C++) |
|---|---|---|
| Производительность | Средняя (JIT/AOT компиляция) | Высокая (машинный код) |
| Задержка аудио | 50–200 мс | 5–15 мс через Oboe |
| 3D-графика | Через Canvas/OpenGL Kotlin API | Vulkan/OpenGL напрямую |
| Сложность разработки | Низкая | Высокая (управление памятью, JNI) |
| Переносимость кода | Только Android | Linux, Windows, macOS, iOS |
| Размер APK | Минимальный | +1–10 МБ на библиотеку |
NDK устанавливается отдельно от Android SDK через SDK Manager. Внутри каталога NDK находятся toolchain (компиляторы), платформенные библиотеки, заголовочные файлы и утилиты. Toolchain NDK включает Clang для кросс-компиляции под все целевые ABI: arm64-v8a, armeabi-v7a, x86_64 и x86. Компилятор автоматически выбирает нужную архитектуру на основе флагов CMake или ndk-build.
Android API для нативного кода представлены заголовочными файлами в каталоге sysroot/usr/include. Там находятся объявления для всех API, доступных в нативном коде: native_activity.h для lifecycle Activity, input.h для событий ввода, sensor.h для датчиков. Подключение этих заголовков даёт доступ к аппаратным возможностям без JNI-прослойки.
ndk/
toolchains/
llvm/prebuilt/windows-x86_64/
bin/ // Clang, ld, llvm-profdata
sysroot/ // Заголовочные файлы Android API
platforms/
android-26/ // libc, libm, libdl для API 26
android-34/
sources/
cxx-stl/ // libc++ заголовки
build/
cmake/ // CMake toolchain file
Через NDK доступны следующие группы нативных API: Native App Glue (управление lifecycle Activity из C), OpenGL ES 3.2 и Vulkan 1.3 (графика), Oboe (низкозадержное аудио), Neural Networks API (машинное обучение на устройстве). Каждое API имеет заголовочный файл и статическую/динамическую библиотеку в составе NDK.
Для работы с нативными API не нужен JNI — функции вызываются напрямую из C/C++ кода. Однако для взаимодействия с UI на Kotlin всё равно требуется JNI. Такая гибридная архитектура встречается в игровых движках: графика и физика на C++ (через Vulkan), меню и UI на Kotlin (через Jetpack Compose).
JNI (Java Native Interface) — стандартный механизм вызова нативного кода из виртуальной машины Java/Kotlin. Разработчик объявляет в Kotlin внешнюю функцию с ключевым словом external и подключает .so-библиотеку через System.loadLibrary. На стороне 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 описывает исходные файлы, библиотеки и флаги компиляции. Android Studio автоматически вызывает CMake при сборке проекта, если настроен externalNativeBuild в build.gradle. CMake генерирует make-файлы для каждой целевой ABI и компилирует нативный код параллельно.
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. Наиболее распространённые ABI на 2026 год: arm64-v8a (99% современных устройств), armeabi-v7a (старые 32-битные устройства), x86_64 (эмулятор и Chromebook).
Указание abiFilters в build.gradle ограничивает сборку только нужными архитектурами, что сокращает время компиляции. Для Google Play рекомендуется включать все ABI, с которыми совместима библиотека — это гарантирует работу на всех устройствах. Google Play Console позволяет настроить доставку ABI-специфичных APK через App Bundle.
#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-bit | Почти все современные телефоны |
| armeabi-v7a | ARMv7-A | 32-bit | Старые устройства (до 2020) |
| x86_64 | x86-64 | 64-bit | Эмулятор, Chromebook |
| x86 | x86 IA-32 | 32-bit | Устаревшие эмуляторы |
Рассмотрим практический пример: математическая библиотека для работы с числами с плавающей точкой. Нативный код на C++ выполняет вычисления эффективнее, чем Kotlin, за счёт прямого доступа к NEON-инструкциям ARM и отсутствия проверок границ массивов в runtime. Этот пример демонстрирует типичный паттерн использования 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_print из библиотеки android/log.h. Сообщения появляются в 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");
}
Часто задаваемые вопросы
Нет. Для большинства приложений достаточно SDK на Kotlin. NDK нужен для высокопроизводительных задач: обработка аудио/видео в реальном времени, 3D-графика, криптография или переиспользование существующих C/C++ проектов.
NDK использует Clang из LLVM toolchain. Начиная с NDK r23 GCC полностью удалён. Clang кросс-компилирует код для всех Android ABI: arm64-v8a, armeabi-v7a, x86_64, x86.
ABI (Application Binary Interface) — формат машинного кода, определяющий совместимость с процессором. NDK собирает .so-библиотеки под каждый ABI отдельно. arm64-v8a — основной ABI для современных Android-устройств.
Да. Android Studio поддерживает LLDB — отладчик нативного кода. Можно ставить breakpoints в C++ файлах, смотреть переменные и стек вызовов. Для работы требуется NDK и плагин LLDB.
Каждая нативная библиотека добавляет 100 КБ — несколько МБ в APK. Для каждой ABI нужна отдельная .so-библиотека. Android App Bundle доставляет пользователю только подходящую архитектуру, уменьшая размер установки.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также