NDK (Native Development Kit) é um kit de ferramentas para escrever partes de uma aplicação em C e C++ para Android. Ao contrário do SDK Android comum, o NDK compila o código em bibliotecas nativas (.so) que funcionam diretamente com o processador sem uma camada de máquina virtual. De acordo com Google NDK Guides, 2026, o NDK é usado para computação de alto desempenho, jogos, processamento de áudio e reutilização de projetos C/C++ existentes. JNI (Java Native Interface) conecta o código nativo com Kotlin e Java.
Principais pontos
NDK (Native Development Kit) é um conjunto de ferramentas para compilação cruzada de código C e C++ em bibliotecas executáveis para Android. O NDK inclui o compilador Clang, a biblioteca padrão libc++, os ficheiros de cabeçalho da API Android e utilitários de compilação. O desenvolvedor escreve código nativo em C ou C++, compila-o em ficheiros .so (objetos partilhados) e liga-os à aplicação através de JNI.
O NDK não substitui o Android SDK, mas complementa-o. Toda a interface do utilizador, ciclo de vida e serviços do sistema permanecem em Kotlin ou Java. O código nativo resolve tarefas específicas: cálculos matemáticos, criptografia, codecs, física em jogos. O Google recomenda usar o NDK apenas quando o SDK não fornece o desempenho ou acesso necessário às capacidades de hardware.
A história do NDK começa em 2009 (NDK r1). Durante este tempo, o kit de ferramentas evoluiu de um conjunto experimental de scripts para um sistema maduro com suporte para CMake, depuração LLDB e criação de perfis. Em 2026, a versão atual é NDK r27 com Clang 19, suporte total a C++20 e libc++ como única biblioteca padrão.
O NDK dá ao desenvolvedor acesso a capacidades de baixo nível do Android através de APIs nativas. Os principais casos de uso incluem: trabalho com OpenGL ES e Vulkan para gráficos 3D, processamento de áudio via Oboe, operações criptográficas através de BoringSSL e otimizações NEON para ARM. Todas estas APIs estão disponíveis a partir de C/C++ e requerem o NDK para compilação.
Além disso, o NDK permite reutilizar bibliotecas C/C++ existentes sem reescrevê-las em Kotlin. Isto é especialmente relevante para projetos com uma longa história em C++ — motores de jogos (Unity, Unreal Engine), bibliotecas de visão computacional (OpenCV) ou pacotes criptográficos (OpenSSL). Nestes casos, o NDK poupa anos de desenvolvimento.
| Componente | Função |
|---|---|
| Clang | Compilador C/C++ com suporte C++20 |
| libc++ | Biblioteca padrão C++ (única no NDK r27) |
| CMake | Sistema de compilação de bibliotecas nativas |
| LLDB | Depurador de código nativo no Android Studio |
| ndk-build | Sistema de compilação legado (substituído pelo CMake) |
A escolha entre NDK e SDK puro depende da tarefa. O Android SDK fornece APIs Java/Kotlin prontas para 90% dos cenários — redes, ficheiros, notificações, câmara. O NDK é utilizado quando estas APIs não fornecem o desempenho ou funcionalidade necessários. Por exemplo, o processamento de áudio em tempo real via SDK é possível, mas com latência de 50–200 ms, enquanto uma biblioteca nativa através de Oboe reduz a latência para 5–15 ms.
O tamanho do APK é outro fator. Adicionar NDK aumenta o tamanho da aplicação, pois cada arquitetura ABI requer uma biblioteca .so separada. No entanto, o uso do Android App Bundle (AAB) mitiga o problema: o Google Play entrega ao utilizador apenas a biblioteca para a sua arquitetura. Um projeto nativo típico adiciona 1–10 MB ao tamanho de instalação por dispositivo.
| Critério | SDK (Kotlin/Java) | NDK (C/C++) |
|---|---|---|
| Desempenho | Médio (compilação JIT/AOT) | Alto (código de máquina) |
| Latência de áudio | 50–200 ms | 5–15 ms via Oboe |
| Gráficos 3D | Via API Kotlin Canvas/OpenGL | Vulkan/OpenGL diretamente |
| Complexidade de desenvolvimento | Baixa | Alta (gestão de memória, JNI) |
| Portabilidade do código | Apenas Android | Linux, Windows, macOS, iOS |
| Tamanho do APK | Mínimo | +1–10 MB por biblioteca |
O NDK é instalado separadamente do Android SDK através do SDK Manager. Dentro do diretório do NDK encontram-se o toolchain (compiladores), as bibliotecas de plataforma, os ficheiros de cabeçalho e utilitários. O Toolchain do NDK inclui o Clang para compilação cruzada para todas as ABIs alvo: arm64-v8a, armeabi-v7a, x86_64 e x86. O compilador seleciona automaticamente a arquitetura necessária com base nas flags do CMake ou ndk-build.
As APIs Android para código nativo são fornecidas por ficheiros de cabeçalho no diretório sysroot/usr/include. Lá encontram-se as declarações para todas as APIs disponíveis em código nativo: native_activity.h para o ciclo de vida da Activity, input.h para eventos de entrada, sensor.h para sensores. Incluir estes cabeçalhos dá acesso a capacidades de hardware sem uma camada JNI.
ndk/
toolchains/
llvm/prebuilt/windows-x86_64/
bin/ // Clang, ld, llvm-profdata
sysroot/ // Ficheiros de cabeçalho da API Android
platforms/
android-26/ // libc, libm, libdl para API 26
android-34/
sources/
cxx-stl/ // Cabeçalhos libc++
build/
cmake/ // Ficheiro toolchain do CMake
Através do NDK estão disponíveis os seguintes grupos de APIs nativas: Native App Glue (gestão do ciclo de vida da Activity a partir de C), OpenGL ES 3.2 e Vulkan 1.3 (gráficos), Oboe (áudio de baixa latência), Neural Networks API (aprendizagem automática no dispositivo). Cada API tem um ficheiro de cabeçalho e uma biblioteca estática/dinâmica como parte do NDK.
Trabalhar com APIs nativas não requer JNI — as funções são chamadas diretamente a partir de código C/C++. No entanto, a interação com a interface do utilizador em Kotlin ainda requer JNI. Esta arquitetura híbrida é comum em motores de jogos: gráficos e física em C++ (via Vulkan), menus e interface do utilizador em Kotlin (via Jetpack Compose).
JNI (Java Native Interface) é um mecanismo padrão para chamar código nativo a partir da máquina virtual Java/Kotlin. O desenvolvedor declara uma função externa em Kotlin com a palavra-chave external e carrega a biblioteca .so através de System.loadLibrary. No lado do C++, a função é declarada usando a convenção de nomenclatura JNI, que codifica o pacote e o nome da 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
}
// Uso no código
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 é o sistema de compilação principal para NDK, tendo substituído o antigo ndk-build. O ficheiro CMakeLists.txt descreve os ficheiros fonte, bibliotecas e flags de compilação. O Android Studio invoca automaticamente o CMake ao compilar o projeto se o externalNativeBuild estiver configurado no build.gradle. O CMake gera makefiles para cada ABI alvo e compila o código nativo em paralelo.
cmake_minimum_required(VERSION 3.22.1)
project("nativelib")
# Incluir ficheiros de cabeçalho
include_directories(src/main/cpp/include)
# Criar biblioteca nativa
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp
src/main/cpp/math_utils.cpp
src/main/cpp/audio_processor.cpp
)
# Vincular bibliotecas do sistema Android
target_link_libraries(
native-lib
android
log
OpenSLES
# libc++ vinculada automaticamente
)
# Flags de otimização
target_compile_options(native-lib PRIVATE -O3 -Wall -Wextra)
android {
defaultConfig {
ndk {
// ABIs alvo para compilação
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) é o formato de código de máquina que determina a compatibilidade com o processador do dispositivo. Cada arquitetura ARM ou x86 tem a sua própria ABI. O NDK compila código nativo separadamente para cada ABI especificada. As ABIs mais comuns em 2026: arm64-v8a (99% dos dispositivos modernos), armeabi-v7a (dispositivos antigos de 32 bits), x86_64 (emulador e Chromebook).
Especificar abiFilters no build.gradle limita a compilação apenas às arquiteturas necessárias, reduzindo o tempo de compilação. Para o Google Play, recomenda-se incluir todas as ABIs suportadas pela biblioteca — isto garante o funcionamento em todos os dispositivos. O Google Play Console permite configurar a entrega de APKs específicos por ABI através do 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 | Arquitetura | Bits | Dispositivos |
|---|---|---|---|
| arm64-v8a | ARMv8-A | 64 bits | Quase todos os telemóveis modernos |
| armeabi-v7a | ARMv7-A | 32 bits | Dispositivos antigos (pré-2020) |
| x86_64 | x86-64 | 64 bits | Emulador, Chromebook |
| x86 | x86 IA-32 | 32 bits | Emuladores legados |
Vamos considerar um exemplo prático: uma biblioteca matemática para trabalhar com números de ponto flutuante. O código nativo em C++ realiza cálculos de forma mais eficiente que o Kotlin, graças ao acesso direto às instruções NEON do ARM e à ausência de verificações de limites de arrays em tempo de execução. Este exemplo demonstra um padrão típico de uso do NDK — transferir cálculos pesados para a camada nativa.
#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);
// Calcular média e desvio padrão
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);
// Normalização: (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;
}
Para depurar código nativo, utilize a macro __android_log_print da biblioteca android/log.h. As mensagens aparecem no Logcat juntamente com os registos Java/Kotlin. O nível de registo (ANDROID_LOG_DEBUG, ANDROID_LOG_ERROR) ajuda a filtrar mensagens.
#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++) {
// Processamento pesado
LOGD("Item %d processed", i);
}
LOGD("Processing complete");
}
Perguntas frequentes
Não. Para a maioria das aplicações, o SDK Kotlin é suficiente. O NDK é necessário para tarefas de alto desempenho: processamento de áudio/vídeo em tempo real, gráficos 3D, criptografia ou reutilização de projetos C/C++ existentes.
O NDK usa o Clang do LLVM toolchain. Desde o NDK r23, o GCC foi completamente removido. O Clang faz compilação cruzada para todas as ABIs Android: arm64-v8a, armeabi-v7a, x86_64, x86.
ABI (Application Binary Interface) é o formato de código de máquina que determina a compatibilidade com o processador. O NDK compila bibliotecas .so para cada ABI separadamente. arm64-v8a é a ABI principal para dispositivos Android modernos.
Sim. O Android Studio suporta LLDB — um depurador de código nativo. Pode definir pontos de interrupção em ficheiros C++, ver variáveis e a pilha de chamadas. São necessários o NDK e o plugin LLDB.
Cada biblioteca nativa adiciona de 100 KB a vários MB ao APK. Cada ABI requer uma biblioteca .so separada. O Android App Bundle entrega ao utilizador apenas a arquitetura adequada, reduzindo o tamanho da instalação.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também