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 (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.
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.
| Composant | Fonction |
|---|---|
| Clang | Compilateur C/C++ avec support C++20 |
| libc++ | Bibliothèque standard C++ (unique dans NDK r27) |
| CMake | Système de compilation des bibliothèques natives |
| LLDB | Débogueur de code natif dans Android Studio |
| ndk-build | Ancien système de compilation (remplacé par CMake) |
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.
| Critère | SDK (Kotlin/Java) | NDK (C/C++) |
|---|---|---|
| Performance | Moyenne (compilation JIT/AOT) | Élevée (code machine) |
| Latence audio | 50–200 ms | 5–15 ms via Oboe |
| Graphiques 3D | Via l'API Kotlin Canvas/OpenGL | Vulkan/OpenGL directement |
| Complexité de développement | Faible | Élevée (gestion mémoire, JNI) |
| Portabilité du code | Android uniquement | Linux, Windows, macOS, iOS |
| Taille de l'APK | Minimale | +1–10 Mo par bibliothèque |
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.
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
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 (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.
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++"
#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 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.
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)
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 (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.
#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 | Architecture | Bits | Appareils |
|---|---|---|---|
| arm64-v8a | ARMv8-A | 64 bits | Presque tous les téléphones modernes |
| armeabi-v7a | ARMv7-A | 32 bits | Anciens appareils (avant 2020) |
| x86_64 | x86-64 | 64 bits | Émulateur, Chromebook |
| x86 | x86 IA-32 | 32 bits | Émulateurs hérités |
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.
#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;
}
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.
#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
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.
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.
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.
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.
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é
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.
Lisez aussi