NDK Android: wat is het, Native Development Kit en JNI voor C++

Auteur: IT Sectr Gepubliceerd: 2026-02-09 Leestijd: 10 min

NDK (Native Development Kit) — is een toolkit voor het schrijven van een deel van de app in C en C++ voor Android. In tegenstelling tot de gewone Android SDK, compileert NDK code naar native bibliotheken (.so) die direct op de processor werken zonder virtuele machine-laag. Volgens Google NDK Guides, 2026, wordt NDK gebruikt voor krachtige berekeningen, games, audioverwerking en hergebruik van bestaande C/C++ projecten. JNI (Java Native Interface) verbindt native code met Kotlin en Java.

Belangrijkste punten

  • NDK — toolkit voor het compileren van C/C++ code naar native bibliotheken voor Android.
  • JNI — interface voor interactie tussen de Java/Kotlin virtuele machine en native code.
  • CMake — het primaire buildsysteem voor NDK projecten, configureerbaar via CMakeLists.txt.
  • ABI — de doelprocessorarchitectuur (arm64-v8a, armeabi-v7a, x86_64) waarvoor de bibliotheek wordt gebouwd.
  • NDK is niet nodig voor de meeste Android-apps en wordt alleen gebruikt bij taken met hoge prestatie-eisen.

Wat is NDK

NDK (Native Development Kit) — is een set tools voor het cross-compileren van C en C++ code naar uitvoerbare bibliotheken voor Android. NDK bevat de Clang compiler, de libc++ standaardbibliotheek, Android API headerbestanden en build-hulpprogramma's. De ontwikkelaar schrijft native code in C of C++, compileert deze naar .so-bestanden (shared objects) en koppelt deze via JNI aan de app.

NDK vervangt Android SDK niet, maar vult het aan. De hele UI, lifecycle en systeemdiensten blijven in Kotlin of Java. Native code lost specifieke taken op: wiskundige berekeningen, cryptografie, codecs, fysica in games. Google adviseert NDK alleen te gebruiken wanneer SDK niet de vereiste prestaties of toegang tot hardwaremogelijkheden biedt.

De geschiedenis van NDK begint in 2009 (NDK r1). In die tijd zijn de tools geëvolueerd van een experimentele set scripts tot een volwassen systeem met ondersteuning voor CMake, LLDB-debugging en profilering. In 2026 is de huidige versie NDK r27 met Clang 19, volledige ondersteuning voor C++20 en libc++ als enige standaardbibliotheek.

Belangrijkste mogelijkheden van NDK

NDK geeft de ontwikkelaar toegang tot low-level mogelijkheden van Android via native API's. De belangrijkste gebruiksscenario's omvatten: werken met OpenGL ES en Vulkan voor 3D-graphics, audioverwerking via Oboe, cryptografische operaties via BoringSSL en NEON-optimalisaties voor ARM. Al deze API's zijn toegankelijk vanuit C/C++ en vereisen NDK voor compilatie.

Daarnaast maakt NDK het mogelijk bestaande C/C++ bibliotheken te hergebruiken zonder ze naar Kotlin te herschrijven. Dit is vooral relevant voor projecten met een jarenlange geschiedenis in C++ — game-engines (Unity, Unreal Engine), computer vision bibliotheken (OpenCV) of cryptografische pakketten (OpenSSL). In zulke gevallen bespaart NDK jaren aan ontwikkelingstijd.

ComponentDoel
ClangC/C++ compiler met C++20 ondersteuning
libc++Standaard C++ bibliotheek (enige in NDK r27)
CMakeBuildsysteem voor native bibliotheken
LLDBNative code debugger in Android Studio
ndk-buildVerouderd buildsysteem (vervangen door CMake)

NDK vs SDK: wanneer is native code nodig

De keuze tussen NDK en pure SDK hangt af van de taak. Android SDK bied kant-en-klare Java/Kotlin API's voor 90% van de scenario's — werken met netwerk, bestanden, meldingen, camera. NDK wordt aangesloten wanneer deze API's niet de vereiste prestaties of functionaliteit bieden. Bijvoorbeeld, real-time audioverwerking via SDK is mogelijk, maar met vertragingen van 50–200 ms, terwijl een native bibliotheek via Oboe de vertraging reduceert tot 5–15 ms.

APK-grootte — een andere factor. Toevoeging van NDK vergroot de app-grootte, omdat elke ABI-architectuur een aparte .so-bibliotheek vereist. Het gebruik van Android App Bundle (AAB) verzacht het probleem echter: Google Play levert de gebruiker alleen de bibliotheek voor zijn architectuur. Een typisch native project voegt 1–10 MB toe aan de installatiegrootte op het apparaat.

Vergelijking van SDK en NDK op criteria

CriteriumSDK (Kotlin/Java)NDK (C/C++)
PrestatiesGemiddeld (JIT/AOT compilatie)Hoog (machinecode)
Audiovertraging50–200 ms5–15 ms via Oboe
3D-graphicsVia Canvas/OpenGL Kotlin APIDirect Vulkan/OpenGL
OntwikkelingscomplexiteitLaagHoog (geheugenbeheer, JNI)
Code-portabiliteitAlleen AndroidLinux, Windows, macOS, iOS
APK-grootteMinimaal+1–10 MB per bibliotheek

NDK architectuur: tools en bibliotheken

NDK wordt apart van Android SDK geïnstalleerd via SDK Manager. Binnen de NDK-directory bevinden zich toolchain (compilers), platformbibliotheken, headerbestanden en hulpprogramma's. NDK Toolchain bevat Clang voor cross-compilatie voor alle doel-ABI's: arm64-v8a, armeabi-v7a, x86_64 en x86. De compiler selecteert automatisch de juiste architectuur op basis van CMake of ndk-build flags.

Android API's voor native code worden vertegenwoordigd door headerbestanden in de directory sysroot/usr/include. Daar bevinden zich declaraties voor alle API's die beschikbaar zijn in native code: native_activity.h voor Activity lifecycle, input.h voor invoergebeurtenissen, sensor.h voor sensoren. Het koppelen van deze headers geeft toegang tot hardwaremogelijkheden zonder JNI-laag.

NDK directorystructuur

text
ndk/
  toolchains/
    llvm/prebuilt/windows-x86_64/
      bin/        // Clang, ld, llvm-profdata
      sysroot/    // Android API headerbestanden
  platforms/
    android-26/  // libc, libm, libdl voor API 26
    android-34/
  sources/
    cxx-stl/     // libc++ headers
  build/
    cmake/       // CMake toolchain bestand

Native Android API's

Via NDK zijn de volgende groepen native API's beschikbaar: Native App Glue (beheer van Activity lifecycle vanuit C), OpenGL ES 3.2 en Vulkan 1.3 (graphics), Oboe (audio met lage latentie), Neural Networks API (machine learning op het apparaat). Elke API heeft een headerbestand en een statische/dynamische bibliotheek in NDK.

Voor het werken met native API's is geen JNI nodig — functies worden direct vanuit C/C++ code aangeroepen. Voor interactie met de UI in Kotlin is echter nog steeds JNI vereist. Deze hybride architectuur komt voor in game-engines: graphics en fysica in C++ (via Vulkan), menu en UI in Kotlin (via Jetpack Compose).

JNI: hoe Kotlin C++ aanroept

JNI (Java Native Interface) — het standaardmechanisme voor het aanroepen van native code vanuit de Java/Kotlin virtuele machine. De ontwikkelaar declareert in Kotlin een externe functie met het sleutelwoord external en koppelt de .so-bibliotheek via System.loadLibrary. Aan de C++-kant wordt de functie gedeclareerd met de JNI-naamgevingsconventie die het pakket en de klassenaam codeert.

Declaratie van een native functie in 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
}

// Gebruik in code
val bridge = NativeBridge()
println(bridge.stringFromJNI())  // "Hello from C++"

Implementatie van JNI-functie in 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 configureren voor NDK

CMake — het primaire buildsysteem voor NDK, dat de verouderde ndk-build heeft vervangen. Het bestand CMakeLists.txt beschrijft de bronbestanden, bibliotheken en compilatievlaggen. Android Studio roept automatisch CMake aan bij het bouwen van het project als externalNativeBuild is geconfigureerd in build.gradle. CMake genereert make-bestanden voor elke doel-ABI en compileert native code parallel.

Voorbeeld CMakeLists.txt voor native bibliotheek

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

# Headerbestanden koppelen
include_directories(src/main/cpp/include)

# Native bibliotheek maken
add_library(
    native-lib
    SHARED
    src/main/cpp/native-lib.cpp
    src/main/cpp/math_utils.cpp
    src/main/cpp/audio_processor.cpp
)

# Android systeembibliotheken koppelen
target_link_libraries(
    native-lib
    android
    log
    OpenSLES
    # libc++ wordt automatisch gekoppeld
)

# Optimalisatievlaggen
target_compile_options(native-lib PRIVATE -O3 -Wall -Wextra)

build.gradle configureren voor NDK

groovy
android {
    defaultConfig {
        ndk {
            // Doel-ABI's voor build
            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 en multi-platform build

ABI (Application Binary Interface) — is het formaat van machinecode dat de compatibiliteit met de processor van het apparaat bepaalt. Elke ARM- of x86-architectuur heeft zijn eigen ABI. NDK compileert native code apart voor elke opgegeven ABI. De meest voorkomende ABI's in 2026: arm64-v8a (99% van moderne apparaten), armeabi-v7a (oude 32-bit apparaten), x86_64 (emulator en Chromebook).

Het specificeren van abiFilters in build.gradle beperkt de build tot alleen de benodigde architecturen, wat de compilatietijd verkort. Voor Google Play wordt aanbevolen alle ABI's op te nemen waarmee de bibliotheek compatibel is — dit garandeert werking op alle apparaten. Google Play Console maakt het mogelijk ABI-specifieke APK-levering via App Bundle te configureren.

Bepalen van apparaat-ABI in native code

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");
}
ABIArchitectuurBitgrootteApparaten
arm64-v8aARMv8-A64-bitBijna alle moderne telefoons
armeabi-v7aARMv7-A32-bitOude apparaten (tot 2020)
x86_64x86-6464-bitEmulator, Chromebook
x86x86 IA-3232-bitVerouderde emulators

Voorbeelden van native code in C++

Laten we een praktisch voorbeeld bekijken: een wiskundige bibliotheek voor het werken met getallen met drijvende komma. Native code in C++ voert berekeningen efficiënter uit dan Kotlin, dankzij directe toegang tot NEON ARM-instructies en het ontbreken van array-grenscontroles in runtime. Dit voorbeeld toont het typische patroon van NDK-gebruik — het verplaatsen van zware berekeningen naar de native laag.

Wiskundige bewerkingen in native code

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);

    // Gemiddelde en standaarddeviatie berekenen
    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);

    // Normalisatie: (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;
}

Loggen vanuit native code

Gebruik voor het debuggen van native code de macro __android_log_print uit de bibliotheek android/log.h. Berichten verschijnen in Logcat naast Java/Kotlin logs. Het logniveau (ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR) helpt bij het filteren van berichten.

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++) {
        // Zware verwerking
        LOGD("Item %d processed", i);
    }

    LOGD("Processing complete");
}

Veelgestelde vragen

Is NDK verplicht voor Android-ontwikkeling?

Nee. Voor de meeste apps is SDK in Kotlin voldoende. NDK is nodig voor taken met hoge prestaties: real-time audio/video verwerking, 3D-graphics, cryptografie of hergebruik van bestaande C/C++ projecten.

Welke compiler gebruikt NDK?

NDK gebruikt Clang uit de LLVM toolchain. Sinds NDK r23 is GCC volledig verwijderd. Clang cross-compileert code voor alle Android ABI's: arm64-v8a, armeabi-v7a, x86_64, x86.

Wat is ABI in de context van NDK?

ABI (Application Binary Interface) — het formaat van machinecode dat de compatibiliteit met de processor bepaalt. NDK bouwt .so-bibliotheken voor elke ABI apart. arm64-v8a — de belangrijkste ABI voor moderne Android-apparaten.

Kan C++ code worden gedebugd via NDK?

Ja. Android Studio ondersteunt LLDB — de native code debugger. Je kunt breakpoints plaatsen in C++ bestanden, variabelen en de call-stack bekijken. Voor gebruik zijn NDK en de LLDB-plugin vereist.

Hoe beïnvloedt NDK de APK-grootte?

Elke native bibliotheek voegt 100 KB — enkele MB's toe aan de APK. Voor elke ABI is een aparte .so-bibliotheek nodig. Android App Bundle levert de gebruiker alleen de juiste architectuur, waardoor de installatiegrootte afneemt.

Samenvatting

  • NDK — set tools voor het cross-compileren van C/C++ code naar native bibliotheken voor Android via de Clang compiler.
  • JNI — interface die Kotlin/Java verbindt met native code via het sleutelwoord external en functienaamgevingsconventies.
  • CMake — het primaire buildsysteem van NDK, configureerbaar via CMakeLists.txt en externalNativeBuild in Gradle.
  • ABI bepaalt de processorarchitectuur — arm64-v8a, armeabi-v7a, x86_64. Elke ABI vereist een aparte build van de bibliotheek.
  • Gebruik NDK alleen voor taken die hoge prestaties vereisen: games, audio, graphics, cryptografie, machine learning op het apparaat.
  • Google Play ondersteunt ABI-splitsing via App Bundle: de gebruiker ontvangt alleen de bibliotheek voor de architectuur van zijn apparaat.
  • Native bibliotheken vergroten de APK aanzienlijk, maar bieden maximale prestaties en lage latentie voor kritieke bewerkingen.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook