NDK (Native Development Kit) — to zestaw narzędzi do pisania części aplikacji w C i C++ na Androida. W przeciwieństwie do zwykłego Android SDK, NDK kompiluje kod do natywnych bibliotek (.so), które działają bezpośrednio na procesorze bez pośrednictwa maszyny wirtualnej. Według Google NDK Guides, 2026, NDK jest używany do obliczeń wysokowydajnych, gier, przetwarzania dźwięku i ponownego wykorzystania istniejących projektów C/C++. JNI (Java Native Interface) łączy kod natywny z Kotlin i Java.
Najważniejsze
NDK (Native Development Kit) — to zestaw narzędzi do krzyżowej kompilacji kodu C i C++ do wykonywalnych bibliotek dla Androida. W skład NDK wchodzą kompilator Clang, standardowa biblioteka libc++, pliki nagłówkowe Android API i narzędzia budowania. Deweloper pisze kod natywny w C lub C++, kompiluje go do plików .so (shared objects) i podłącza do aplikacji przez JNI.
NDK nie zastępuje Android SDK, ale go uzupełnia. Cały interfejs użytkownika, lifecycle i usługi systemowe pozostają w Kotlin lub Java. Kod natywny rozwiązuje wąskie zadania: obliczenia matematyczne, kryptografia, kodeki, fizyka w grach. Google zaleca używanie NDK tylko wtedy, gdy SDK nie zapewnia wymaganej wydajności lub dostępu do możliwości sprzętowych.
Historia NDK zaczyna się w 2009 roku (NDK r1). W tym czasie narzędzia przeszły drogę od eksperymentalnego zestawu skryptów do dojrzałego systemu z obsługą CMake, debugowania LLDB i profilowania. Na 2026 rok aktualna wersja to NDK r27 z Clang 19, pełną obsługą C++20 i libc++ jako jedyną standardową biblioteką.
NDK daje deweloperowi dostęp do niskopoziomowych możliwości Androida przez natywne API. Główne scenariusze użycia obejmują: pracę z OpenGL ES i Vulkan dla grafiki 3D, przetwarzanie dźwięku przez Oboe, operacje kryptograficzne przez BoringSSL i optymalizacje NEON dla ARM. Wszystkie te API są dostępne z C/C++ i wymagają NDK do kompilacji.
Ponadto NDK pozwala ponownie wykorzystać istniejące biblioteki C/C++ bez przepisywania na Kotlin. Jest to szczególnie istotne w projektach z wieloletnią historią w C++ — silnikach gier (Unity, Unreal Engine), bibliotekach widzenia komputerowego (OpenCV) lub pakietach kryptograficznych (OpenSSL). W takich przypadkach NDK oszczędza lata rozwoju.
| Komponent | Przeznaczenie |
|---|---|
| Clang | Kompilator C/C++ z obsługą C++20 |
| libc++ | Standardowa biblioteka C++ (jedyna w NDK r27) |
| CMake | System budowania bibliotek natywnych |
| LLDB | Debuger kodu natywnego w Android Studio |
| ndk-build | Przestarzały system budowania (zastąpiony przez CMake) |
Wybór między NDK a czystym SDK zależy od zadania. Android SDK zapewnia gotowe API Java/Kotlin dla 90% scenariuszy — praca z siecią, plikami, powiadomieniami, kamerą. NDK jest podłączany, gdy te API nie zapewniają wymaganej wydajności lub funkcjonalności. Na przykład przetwarzanie dźwięku w czasie rzeczywistym przez SDK jest możliwe, ale z opóźnieniami 50–200 ms, podczas gdy natywna biblioteka przez Oboe redukuje opóźnienie do 5–15 ms.
Rozmiar APK — kolejny czynnik. Dodanie NDK zwiększa rozmiar aplikacji, ponieważ każda architektura ABI wymaga osobnej biblioteki .so. Jednak użycie Android App Bundle (AAB) łagodzi problem: Google Play dostarcza użytkownikowi tylko bibliotekę dla jego architektury. Typowy projekt natywny dodaje 1–10 MB do rozmiaru instalacji na urządzeniu.
| Kryterium | SDK (Kotlin/Java) | NDK (C/C++) |
|---|---|---|
| Wydajność | Średnia (kompilacja JIT/AOT) | Wysoka (kod maszynowy) |
| Opóźnienie dźwięku | 50–200 ms | 5–15 ms przez Oboe |
| Grafika 3D | Przez Canvas/OpenGL Kotlin API | Vulkan/OpenGL bezpośrednio |
| Złożoność rozwoju | Niska | Wysoka (zarządzanie pamięcią, JNI) |
| Przenośność kodu | Tylko Android | Linux, Windows, macOS, iOS |
| Rozmiar APK | Minimalny | +1–10 MB na bibliotekę |
NDK instaluje się oddzielnie od Android SDK przez SDK Manager. Wewnątrz katalogu NDK znajdują się toolchain (kompilatory), biblioteki platformowe, pliki nagłówkowe i narzędzia. Toolchain NDK zawiera Clang do krzyżowej kompilacji dla wszystkich docelowych ABI: arm64-v8a, armeabi-v7a, x86_64 i x86. Kompilator automatycznie wybiera odpowiednią architekturę na podstawie flag CMake lub ndk-build.
Android API dla kodu natywnego są reprezentowane przez pliki nagłówkowe w katalogu sysroot/usr/include. Znajdują się tam deklaracje dla wszystkich API dostępnych w kodzie natywnym: native_activity.h dla lifecycle Activity, input.h dla zdarzeń wejścia, sensor.h dla czujników. Podłączenie tych nagłówków daje dostęp do możliwości sprzętowych bez pośrednictwa JNI.
ndk/
toolchains/
llvm/prebuilt/windows-x86_64/
bin/ // Clang, ld, llvm-profdata
sysroot/ // Pliki nagłówkowe Android API
platforms/
android-26/ // libc, libm, libdl dla API 26
android-34/
sources/
cxx-stl/ // Nagłówki libc++
build/
cmake/ // Plik toolchain CMake
Przez NDK dostępne są następujące grupy natywnych API: Native App Glue (zarządzanie lifecycle Activity z C), OpenGL ES 3.2 i Vulkan 1.3 (grafika), Oboe (dźwięk o niskim opóźnieniu), Neural Networks API (uczenie maszynowe na urządzeniu). Każde API ma plik nagłówkowy i bibliotekę statyczną/dynamiczną w składzie NDK.
Do pracy z natywnymi API nie jest potrzebny JNI — funkcje są wywoływane bezpośrednio z kodu C/C++. Jednak do interakcji z UI w Kotlin nadal wymagany jest JNI. Taka hybrydowa architektura występuje w silnikach gier: grafika i fizyka w C++ (przez Vulkan), menu i UI w Kotlin (przez Jetpack Compose).
JNI (Java Native Interface) — standardowy mechanizm wywoływania kodu natywnego z maszyny wirtualnej Java/Kotlin. Deweloper deklaruje w Kotlin zewnętrzną funkcję ze słowem kluczowym external i podłącza bibliotekę .so przez System.loadLibrary. Po stronie C++ funkcja jest deklarowana z użyciem konwencji nazewnictwa JNI, która koduje pakiet i nazwę klasy.
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
}
// Użycie w kodzie
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 — główny system budowania dla NDK, który zastąpił przestarzały ndk-build. Plik CMakeLists.txt opisuje pliki źródłowe, biblioteki i flagi kompilacji. Android Studio automatycznie wywołuje CMake podczas budowania projektu, jeśli skonfigurowano externalNativeBuild w build.gradle. CMake generuje pliki make dla każdego docelowego ABI i kompiluje kod natywny równolegle.
cmake_minimum_required(VERSION 3.22.1)
project("nativelib")
# Dołączanie plików nagłówkowych
include_directories(src/main/cpp/include)
# Tworzenie biblioteki natywnej
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp
src/main/cpp/math_utils.cpp
src/main/cpp/audio_processor.cpp
)
# Dołączanie bibliotek systemowych Androida
target_link_libraries(
native-lib
android
log
OpenSLES
# libc++ dołącza się automatycznie
)
# Flagi optymalizacji
target_compile_options(native-lib PRIVATE -O3 -Wall -Wextra)
android {
defaultConfig {
ndk {
// Docelowe ABI do budowania
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) — to format kodu maszynowego, który określa zgodność z procesorem urządzenia. Każda architektura ARM lub x86 ma własne ABI. NDK kompiluje kod natywny osobno dla każdego określonego ABI. Najbardziej rozpowszechnione ABI na 2026 rok: arm64-v8a (99% nowoczesnych urządzeń), armeabi-v7a (stare urządzenia 32-bitowe), x86_64 (emulator i Chromebook).
Określenie abiFilters w build.gradle ogranicza budowanie tylko do potrzebnych architektur, co skraca czas kompilacji. Dla Google Play zaleca się dołączanie wszystkich ABI, z którymi biblioteka jest zgodna — gwarantuje to działanie na wszystkich urządzeniach. Google Play Console pozwala skonfigurować dostarczanie ABI-specyficznych APK przez 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 | Architektura | Rozmiar słowa | Urządzenia |
|---|---|---|---|
| arm64-v8a | ARMv8-A | 64-bit | Prawie wszystkie nowoczesne telefony |
| armeabi-v7a | ARMv7-A | 32-bit | Stare urządzenia (przed 2020) |
| x86_64 | x86-64 | 64-bit | Emulator, Chromebook |
| x86 | x86 IA-32 | 32-bit | Przestarzałe emulatory |
Rozważmy praktyczny przykład: biblioteka matematyczna do pracy z liczbami zmiennoprzecinkowymi. Kod natywny w C++ wykonuje obliczenia wydajniej niż Kotlin, dzięki bezpośredniemu dostępowi do instrukcji NEON ARM i braku sprawdzania granic tablic w runtime. Ten przykład demonstruje typowy wzorzec użycia NDK — przeniesienie ciężkich obliczeń do warstwy natywnej.
#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);
// Obliczamy średnią i odchylenie standardowe
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);
// Normalizacja: (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;
}
Do debugowania kodu natywnego użyj makra __android_log_print z biblioteki android/log.h. Komunikaty pojawiają się w Logcat obok logów Java/Kotlin. Poziom logowania (ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR) pomaga filtrować komunikaty.
#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++) {
// Ciężkie przetwarzanie
LOGD("Item %d processed", i);
}
LOGD("Processing complete");
}
Często zadawane pytania
Nie. Dla większości aplikacji wystarczy SDK w Kotlin. NDK jest potrzebny do zadań wymagających wysokiej wydajności: przetwarzanie dźwięku/wideo w czasie rzeczywistym, grafika 3D, kryptografia lub ponowne wykorzystanie istniejących projektów C/C++.
NDK używa Clang z LLVM toolchain. Od NDK r23 GCC zostało całkowicie usunięte. Clang krzyżowo kompiluje kod dla wszystkich Android ABI: arm64-v8a, armeabi-v7a, x86_64, x86.
ABI (Application Binary Interface) — format kodu maszynowego określający zgodność z procesorem. NDK buduje biblioteki .so dla każdego ABI osobno. arm64-v8a — główne ABI dla nowoczesnych urządzeń Android.
Tak. Android Studio obsługuje LLDB — debuger kodu natywnego. Można ustawiać breakpointy w plikach C++, przeglądać zmienne i stos wywołań. Do pracy wymagany jest NDK i plugin LLDB.
Każda natywna biblioteka dodaje 100 KB — kilka MB do APK. Dla każdego ABI potrzebna jest osobna biblioteka .so. Android App Bundle dostarcza użytkownikowi tylko odpowiednią architekturę, zmniejszając rozmiar instalacji.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również