NDK Android : qu'est-ce que c'est, Native Development Kit et JNI pour C++

Auteur : IT Sectr Publié le : 2026-02-09 Temps de lecture : 10 min

Le NDK (Native Development Kit) est une boîte à outils pour écrire des parties d'une application en C et C++ pour Android. Contrairement au SDK Android classique, le NDK compile le code en bibliothèques natives (.so) qui fonctionnent directement avec le processeur sans couche de machine virtuelle. Selon Google NDK Guides, 2026, le NDK est utilisé pour le calcul haute performance, les jeux, le traitement audio et la réutilisation de projets C/C++ existants. JNI (Java Native Interface) relie le code natif à Kotlin et Java.

Points clés

  • NDK — une boîte à outils pour compiler du code C/C++ en bibliothèques natives pour Android.
  • JNI — une interface d'interaction entre la machine virtuelle Java/Kotlin et le code natif.
  • CMake — le système de compilation principal pour les projets NDK, configurable via CMakeLists.txt.
  • ABI — l'architecture cible du processeur (arm64-v8a, armeabi-v7a, x86_64) pour laquelle la bibliothèque est compilée.
  • NDK n'est pas nécessaire pour la plupart des applications Android et n'est utilisé que pour les tâches aux exigences de performances élevées.

Qu'est-ce que NDK

NDK (Native Development Kit) est un ensemble d'outils pour la compilation croisée de code C et C++ en bibliothèques exécutables pour Android. Le NDK comprend le compilateur Clang, la bibliothèque standard libc++, les fichiers d'en-tête de l'API Android et des utilitaires de compilation. Le développeur écrit du code natif en C ou C++, le compile en fichiers .so (objets partagés) et les connecte à l'application via JNI.

Le NDK ne remplace pas le SDK Android, mais le complète. Toute l'interface utilisateur, le cycle de vie et les services système restent en Kotlin ou Java. Le code natif résout des tâches spécifiques : calculs mathématiques, cryptographie, codecs, physique dans les jeux. Google recommande d'utiliser le NDK uniquement lorsque le SDK n'offre pas les performances ou l'accès nécessaire aux capacités matérielles.

L'histoire du NDK commence en 2009 (NDK r1). Depuis, la boîte à outils est passée d'un ensemble expérimental de scripts à un système mature avec prise en charge de CMake, du débogage LLDB et du profilage. En 2026, la version actuelle est NDK r27 avec Clang 19, prise en charge complète de C++20 et libc++ comme seule bibliothèque standard.

Capacités clés du NDK

Le NDK donne au développeur un accès aux capacités de bas niveau d'Android via des API natives. Les principaux cas d'utilisation incluent : le travail avec OpenGL ES et Vulkan pour les graphiques 3D, le traitement audio via Oboe, les opérations cryptographiques via BoringSSL et les optimisations NEON pour ARM. Toutes ces API sont disponibles depuis C/C++ et nécessitent le NDK pour la compilation.

De plus, le NDK permet de réutiliser des bibliothèques C/C++ existantes sans les réécrire en Kotlin. Ceci est particulièrement pertinent pour les projets avec une longue histoire en C++ — moteurs de jeux (Unity, Unreal Engine), bibliothèques de vision par ordinateur (OpenCV) ou paquets cryptographiques (OpenSSL). Dans ces cas, le NDK économise des années de développement.

ComposantFonction
ClangCompilateur C/C++ avec support C++20
libc++Bibliothèque standard C++ (unique dans NDK r27)
CMakeSystème de compilation des bibliothèques natives
LLDBDébogueur de code natif dans Android Studio
ndk-buildAncien système de compilation (remplacé par CMake)

NDK vs SDK : quand le code natif est nécessaire

Le choix entre NDK et SDK pur dépend de la tâche. Le SDK Android fournit des API Java/Kotlin prêtes à l'emploi pour 90% des scénarios — réseaux, fichiers, notifications, caméra. Le NDK est utilisé lorsque ces API n'offrent pas les performances ou fonctionnalités requises. Par exemple, le traitement audio en temps réel via SDK est possible mais avec une latence de 50 à 200 ms, tandis qu'une bibliothèque native via Oboe réduit la latence à 5–15 ms.

La taille de l'APK est un autre facteur. L'ajout du NDK augmente la taille de l'application car chaque architecture ABI nécessite une bibliothèque .so distincte. Cependant, l'utilisation d'Android App Bundle (AAB) atténue le problème : Google Play ne livre à l'utilisateur que la bibliothèque correspondant à son architecture. Un projet natif typique ajoute 1 à 10 Mo à la taille d'installation par appareil.

Comparaison SDK vs NDK par critères

CritèreSDK (Kotlin/Java)NDK (C/C++)
PerformanceMoyenne (compilation JIT/AOT)Élevée (code machine)
Latence audio50–200 ms5–15 ms via Oboe
Graphiques 3DVia l'API Kotlin Canvas/OpenGLVulkan/OpenGL directement
Complexité de développementFaibleÉlevée (gestion mémoire, JNI)
Portabilité du codeAndroid uniquementLinux, Windows, macOS, iOS
Taille de l'APKMinimale+1–10 Mo par bibliothèque

Architecture NDK : outils et bibliothèques

Le NDK est installé séparément du SDK Android via le SDK Manager. Dans le répertoire NDK se trouvent la toolchain (compilateurs), les bibliothèques de plateforme, les fichiers d'en-tête et les utilitaires. La toolchain NDK inclut Clang pour la compilation croisée vers toutes les ABI cibles : arm64-v8a, armeabi-v7a, x86_64 et x86. Le compilateur sélectionne automatiquement l'architecture requise en fonction des flags CMake ou ndk-build.

Les API Android pour le code natif sont fournies par des fichiers d'en-tête dans le répertoire sysroot/usr/include. On y trouve les déclarations pour toutes les API disponibles en code natif : native_activity.h pour le cycle de vie de l'Activity, input.h pour les événements d'entrée, sensor.h pour les capteurs. L'inclusion de ces en-têtes donne accès aux capacités matérielles sans couche JNI.

Structure des répertoires NDK

text
ndk/
  toolchains/
    llvm/prebuilt/windows-x86_64/
      bin/        // Clang, ld, llvm-profdata
      sysroot/    // Fichiers d'en-tête de l'API Android
  platforms/
    android-26/  // libc, libm, libdl pour API 26
    android-34/
  sources/
    cxx-stl/     // En-têtes libc++
  build/
    cmake/       // Fichier toolchain CMake

API natives Android

Les groupes d'API natives suivants sont disponibles via NDK : Native App Glue (gestion du cycle de vie de l'Activity depuis C), OpenGL ES 3.2 et Vulkan 1.3 (graphiques), Oboe (audio à faible latence), Neural Networks API (apprentissage automatique sur l'appareil). Chaque API possède un fichier d'en-tête et une bibliothèque statique/dynamique faisant partie du NDK.

Travailler avec des API natives ne nécessite pas JNI — les fonctions sont appelées directement depuis le code C/C++. Cependant, l'interaction avec l'interface utilisateur en Kotlin nécessite toujours JNI. Cette architecture hybride est courante dans les moteurs de jeux : graphiques et physique en C++ (via Vulkan), menus et interface utilisateur en Kotlin (via Jetpack Compose).

JNI : comment Kotlin appelle C++

JNI (Java Native Interface) est un mécanisme standard pour appeler du code natif depuis la machine virtuelle Java/Kotlin. Le développeur déclare une fonction externe en Kotlin avec le mot-clé external et charge la bibliothèque .so via System.loadLibrary. Du côté C++, la fonction est déclarée en utilisant la convention de nommage JNI, qui encode le paquetage et le nom de la classe.

Déclaration d'une fonction native en 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
}

// Utilisation dans le code
val bridge = NativeBridge()
println(bridge.stringFromJNI())  // "Hello from C++"

Implémentation de la fonction JNI en 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;
}

Configuration de CMakeLists.txt pour NDK

CMake est le système de compilation principal pour NDK, ayant remplacé l'ancien ndk-build. Le fichier CMakeLists.txt décrit les fichiers sources, les bibliothèques et les flags de compilation. Android Studio invoque automatiquement CMake lors de la compilation du projet si externalNativeBuild est configuré dans build.gradle. CMake génère des makefiles pour chaque ABI cible et compile le code natif en parallèle.

Exemple de CMakeLists.txt pour une bibliothèque native

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

# Inclure les fichiers d'en-tête
include_directories(src/main/cpp/include)

# Créer une bibliothèque native
add_library(
    native-lib
    SHARED
    src/main/cpp/native-lib.cpp
    src/main/cpp/math_utils.cpp
    src/main/cpp/audio_processor.cpp
)

# Lier les bibliothèques système Android
target_link_libraries(
    native-lib
    android
    log
    OpenSLES
    # libc++ liée automatiquement
)

# Flags d'optimisation
target_compile_options(native-lib PRIVATE -O3 -Wall -Wextra)

Configuration de build.gradle pour NDK

groovy
android {
    defaultConfig {
        ndk {
            // ABI cibles pour la compilation
            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 et compilation multiplateforme

ABI (Application Binary Interface) est le format de code machine qui détermine la compatibilité avec le processeur de l'appareil. Chaque architecture ARM ou x86 a sa propre ABI. NDK compile le code natif séparément pour chaque ABI spécifiée. Les ABI les plus courantes en 2026 : arm64-v8a (99% des appareils modernes), armeabi-v7a (anciens appareils 32 bits), x86_64 (émulateur et Chromebook).

Spécifier abiFilters dans build.gradle limite la compilation aux seules architectures nécessaires, réduisant le temps de compilation. Pour Google Play, il est recommandé d'inclure toutes les ABI que la bibliothèque prend en charge — cela garantit son fonctionnement sur tous les appareils. Google Play Console permet de configurer la livraison d'APK spécifiques par ABI via App Bundle.

Détermination de l'ABI de l'appareil en code natif

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");
}
ABIArchitectureBitsAppareils
arm64-v8aARMv8-A64 bitsPresque tous les téléphones modernes
armeabi-v7aARMv7-A32 bitsAnciens appareils (avant 2020)
x86_64x86-6464 bitsÉmulateur, Chromebook
x86x86 IA-3232 bitsÉmulateurs hérités

Exemples de code natif en C++

Prenons un exemple pratique : une bibliothèque mathématique pour travailler avec des nombres à virgule flottante. Le code natif en C++ effectue les calculs plus efficacement que Kotlin, grâce à l'accès direct aux instructions NEON d'ARM et à l'absence de vérifications des limites de tableau à l'exécution. Cet exemple illustre un modèle d'utilisation typique du NDK — le déchargement des calculs lourds vers la couche native.

Opérations mathématiques en code natif

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

    // Calculer la moyenne et l'écart type
    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);

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

Journalisation à partir du code natif

Pour déboguer le code natif, utilisez la macro __android_log_print de la bibliothèque android/log.h. Les messages apparaissent dans Logcat aux côtés des logs Java/Kotlin. Le niveau de journalisation (ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR) aide à filtrer les messages.

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

    LOGD("Processing complete");
}

Questions fréquentes

Est-il obligatoire d'utiliser NDK pour le développement Android ?

Non. Pour la plupart des applications, le SDK Kotlin suffit. NDK est nécessaire pour les tâches hautes performances : traitement audio/vidéo en temps réel, graphiques 3D, cryptographie ou réutilisation de projets C/C++ existants.

Quel compilateur NDK utilise-t-il ?

NDK utilise Clang de la toolchain LLVM. Depuis NDK r23, GCC a été complètement supprimé. Clang compile en code natif pour toutes les ABI Android : arm64-v8a, armeabi-v7a, x86_64, x86.

Qu'est-ce que l'ABI dans le contexte de NDK ?

ABI (Application Binary Interface) est le format de code machine qui détermine la compatibilité avec le processeur. NDK compile des bibliothèques .so pour chaque ABI séparément. arm64-v8a est l'ABI principale pour les appareils Android modernes.

Peut-on déboguer du code C++ via NDK ?

Oui. Android Studio prend en charge LLDB — un débogueur de code natif. Vous pouvez définir des points d'arrêt dans les fichiers C++, visualiser les variables et la pile d'appels. NDK et le plugin LLDB sont requis.

Comment NDK affecte-t-il la taille de l'APK ?

Chaque bibliothèque native ajoute de 100 Ko à plusieurs Mo à l'APK. Chaque ABI nécessite une bibliothèque .so distincte. Android App Bundle ne livre à l'utilisateur que l'architecture appropriée, réduisant la taille d'installation.

Résumé

  • NDK — un ensemble d'outils pour la compilation croisée de code C/C++ en bibliothèques natives pour Android avec le compilateur Clang.
  • JNI — une interface reliant Kotlin/Java au code natif via le mot-clé external et les conventions de nommage de fonctions.
  • CMake — le système de compilation principal de NDK, configuré via CMakeLists.txt et externalNativeBuild dans Gradle.
  • ABI définit l'architecture du processeur — arm64-v8a, armeabi-v7a, x86_64. Chaque ABI nécessite une compilation séparée de la bibliothèque.
  • Utilisez NDK uniquement pour les tâches nécessitant des performances élevées : jeux, audio, graphiques, cryptographie, apprentissage automatique sur l'appareil.
  • Google Play prend en charge la répartition par ABI via App Bundle : l'utilisateur reçoit uniquement la bibliothèque pour l'architecture de son appareil.
  • Les bibliothèques natives augmentent considérablement la taille de l'APK, mais offrent des performances maximales et une faible latence pour les opérations critiques.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi