NDK Android: какво е, Native Development Kit и JNI за C++

Автор: IT Sectr Публикувано: 2026-02-09 Време за четене: 10 мин

NDK (Native Development Kit) — е инструментариум за писане на част от приложението на C и C++ за Android. За разлика от обикновения Android SDK, NDK компилира кода в native библиотеки (.so), които работят директно с процесора без слой на виртуална машина. Според Google NDK Guides, 2026, NDK се използва за високопроизводителни изчисления, игри, обработка на аудио и преизползване на съществуващи C/C++ проекти. JNI (Java Native Interface) свързва native кода с Kotlin и Java.

Основни моменти

  • NDK — инструментариум за компилиране на C/C++ код в native библиотеки за Android.
  • JNI — интерфейс за взаимодействие между виртуалната машина Java/Kotlin и native кода.
  • CMake — основната система за изграждане за NDK проекти, конфигурирана чрез CMakeLists.txt.
  • ABI — целевата архитектура на процесора (arm64-v8a, armeabi-v7a, x86_64), за която се изгражда библиотеката.
  • NDK не е необходим за повечето Android приложения и се прилага само в задачи с високи изисквания за производителност.

Какво е NDK

NDK (Native Development Kit) — е набор от инструменти за кръстосано компилиране на C и C++ код в изпълними библиотеки за Android. NDK включва компилатора Clang, стандартната библиотека libc++, заглавни файлове на Android API и помощни програми за изграждане. Разработчикът пише native код на C или C++, компилира го в .so файлове (shared objects) и го свързва с приложението чрез JNI.

NDK не замества Android SDK, а го допълва. Целият UI, lifecycle и системни услуги остават в Kotlin или Java. Native кодът решава тесни задачи: математически изчисления, криптография, кодеци, физика в игри. Google препоръчва използването на NDK само когато SDK не осигурява необходимата производителност или достъп до хардуерни възможности.

Историята на NDK започва от 2009 г. (NDK r1). През това време инструментите преминаха от експериментален набор от скриптове до зряла система с поддръжка на CMake, LLDB дебъгване и профилиране. Към 2026 г. актуалната версия е NDK r27 с Clang 19, пълна поддръжка на C++20 и libc++ като единствена стандартна библиотека.

Ключови възможности на NDK

NDK предоставя на разработчика достъп до нисконивови възможности на Android чрез native 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Система за изграждане на native библиотеки
LLDBДебъгер на native код в Android Studio
ndk-buildОстаряла система за изграждане (заместена от CMake)

NDK срещу SDK: кога е необходим native код

Изборът между NDK и чист SDK зависи от задачата. Android SDK предоставя готови Java/Kotlin API за 90% от сценариите — работа с мрежа, файлове, известия, камера. NDK се свързва, когато тези API не осигуряват необходимата производителност или функционалност. Например, обработка на аудио в реално време чрез SDK е възможна, но със закъснение от 50–200 ms, докато native библиотека чрез Oboe намалява закъснението до 5–15 ms.

Размер на APK — друг фактор. Добавянето на NDK увеличава размера на приложението, тъй като всяка ABI архитектура изисква отделна .so библиотека. Използването на Android App Bundle (AAB) обаче смекчава проблема: Google Play доставя на потребителя само библиотеката за неговата архитектура. Типичен native проект добавя 1–10 MB към размера на инсталацията на устройството.

Сравнение на SDK и NDK по критерии

КритерийSDK (Kotlin/Java)NDK (C/C++)
ПроизводителностСредна (JIT/AOT компилиране)Висока (машинен код)
Закъснение на аудио50–200 ms5–15 ms чрез Oboe
3D графикаЧрез Canvas/OpenGL Kotlin APIДиректно Vulkan/OpenGL
Сложност на разработкаНискаВисока (управление на памет, JNI)
Преносимост на кодСамо AndroidLinux, Windows, macOS, iOS
Размер на APKМинимален+1–10 MB на библиотека

Архитектура на NDK: инструменти и библиотеки

NDK се инсталира отделно от Android SDK чрез SDK Manager. Вътре в директорията на NDK се намират toolchain (компилатори), платформени библиотеки, заглавни файлове и помощни програми. NDK Toolchain включва Clang за кръстосано компилиране за всички целеви ABI: arm64-v8a, armeabi-v7a, x86_64 и x86. Компилаторът автоматично избира правилната архитектура въз основа на флагове на CMake или ndk-build.

Android API за native код са представени от заглавни файлове в директорията sysroot/usr/include. Там се намират декларации за всички API, достъпни в native код: native_activity.h за lifecycle на Activity, input.h за входни събития, sensor.h за сензори. Свързването на тези заглавни файлове предоставя достъп до хардуерни възможности без JNI слой.

Структура на директориите на NDK

text
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 файл

Native Android API

Чрез NDK са достъпни следните групи native API: Native App Glue (управление на lifecycle на Activity от C), OpenGL ES 3.2 и Vulkan 1.3 (графика), Oboe (аудио с ниско закъснение), Neural Networks API (машинно обучение на устройството). Всяко API има заглавен файл и статична/динамична библиотека в състава на NDK.

За работа с native API не е необходим JNI — функциите се извикват директно от C/C++ код. Въпреки това, за взаимодействие с UI в Kotlin все още е необходим JNI. Такава хибридна архитектура се среща в игрови двигатели: графика и физика на C++ (чрез Vulkan), меню и UI на Kotlin (чрез Jetpack Compose).

JNI: как Kotlin извиква C++

JNI (Java Native Interface) — стандартният механизъм за извикване на native код от виртуалната машина Java/Kotlin. Разработчикът декларира в Kotlin външна функция с ключовата дума external и свързва .so библиотеката чрез System.loadLibrary. От страна на C++, функцията се декларира с помощта на конвенцията за именуване на JNI, която кодира пакета и името на класа.

Декларация на native функция в Kotlin

kotlin
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++"

Имплементация на JNI функция в C++

cpp
#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;
}

Настройка на CMakeLists.txt за NDK

CMake — основната система за изграждане за NDK, която замени остарелия ndk-build. Файлът CMakeLists.txt описва изходните файлове, библиотеките и флаговете за компилиране. Android Studio автоматично извиква CMake при изграждане на проекта, ако externalNativeBuild е конфигуриран в build.gradle. CMake генерира make файлове за всеки целеви ABI и компилира native код паралелно.

Пример за CMakeLists.txt за native библиотека

cmake
cmake_minimum_required(VERSION 3.22.1)
project("nativelib")

# Свързване на заглавни файлове
include_directories(src/main/cpp/include)

# Създаване на native библиотека
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)

Настройка на build.gradle за NDK

groovy
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 и многоплатформено изграждане

ABI (Application Binary Interface) — формат на машинен код, който определя съвместимостта с процесора на устройството. Всяка ARM или x86 архитектура има свой ABI. NDK компилира native код отделно за всяко указано 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.

Определяне на ABI на устройството в native код

cpp
#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-v8aARMv8-A64-битовПочти всички съвременни телефони
armeabi-v7aARMv7-A32-битовСтари устройства (до 2020 г.)
x86_64x86-6464-битовЕмулатор, Chromebook
x86x86 IA-3232-битовОстарели емулатори

Примери за native код в C++

Нека разгледаме практически пример: математическа библиотека за работа с числа с плаваща запетая. Native кодът в C++ извършва изчисления по-ефективно от Kotlin, благодарение на директния достъп до NEON ARM инструкции и липсата на проверки на границите на масивите по време на изпълнение. Този пример демонстрира типичния модел на използване на NDK — прехвърляне на тежки изчисления в native слоя.

Математически операции в native код

cpp
#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;
}

Логиране от native код

За дебъгване на native код използвайте макроса __android_log_print от библиотеката android/log.h. Съобщенията се появяват в Logcat до Java/Kotlin логовете. Нивото на логиране (ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR) помага за филтриране на съобщения.

cpp
#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");
}

Често задавани въпроси

Задължително ли е използването на NDK за Android разработка?

Не. За повечето приложения е достатъчен SDK в Kotlin. NDK е необходим за високопроизводителни задачи: обработка на аудио/видео в реално време, 3D графика, криптография или преизползване на съществуващи C/C++ проекти.

Какъв компилатор използва NDK?

NDK използва Clang от LLVM toolchain. От NDK r23 нататък GCC е напълно премахнат. Clang кръстосано компилира код за всички Android ABI: arm64-v8a, armeabi-v7a, x86_64, x86.

Какво е ABI в контекста на NDK?

ABI (Application Binary Interface) — формат на машинен код, определящ съвместимостта с процесора. NDK изгражда .so библиотеки за всяко ABI отделно. arm64-v8a — основното ABI за съвременни Android устройства.

Може ли да се дебъгва C++ код чрез NDK?

Да. Android Studio поддържа LLDB — дебъгер за native код. Могат да се поставят breakpoint-и в C++ файлове, да се преглеждат променливи и стекът на извикванията. За работа са необходими NDK и LLDB приставка.

Как NDK влияе на размера на APK?

Всяка native библиотека добавя 100 KB — няколко MB към APK. За всяко ABI е необходима отделна .so библиотека. Android App Bundle доставя на потребителя само подходящата архитектура, намалявайки размера на инсталацията.

Резюме

  • NDK — набор от инструменти за кръстосано компилиране на C/C++ код в native библиотеки за Android чрез компилатора Clang.
  • JNI — интерфейс, свързващ Kotlin/Java с native код чрез ключовата дума external и конвенцията за именуване на функции.
  • CMake — основната система за изграждане на NDK, конфигурирана чрез файла CMakeLists.txt и externalNativeBuild в Gradle.
  • ABI определя архитектурата на процесора — arm64-v8a, armeabi-v7a, x86_64. Всяко ABI изисква отделно изграждане на библиотеката.
  • Използвайте NDK само за задачи, изискващи висока производителност: игри, аудио, графика, криптография, машинно обучение на устройството.
  • Google Play поддържа ABI разделяне чрез App Bundle: потребителят получава само библиотеката за архитектурата на своето устройство.
  • Native библиотеките значително увеличават APK, но осигуряват максимална производителност и ниско закъснение за критични операции.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също