NDK Android: o que é, Native Development Kit e JNI para C++

Autor: IT Sectr Publicado: 2026-02-09 Tempo de leitura: 10 min

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 — um kit de ferramentas para compilar código C/C++ em bibliotecas nativas para Android.
  • JNI — uma interface de interação entre a máquina virtual Java/Kotlin e o código nativo.
  • CMake — o sistema de compilação principal para projetos NDK, configurável via CMakeLists.txt.
  • ABI — a arquitetura alvo do processador (arm64-v8a, armeabi-v7a, x86_64) para a qual a biblioteca é compilada.
  • NDK não é necessário para a maioria das aplicações Android e é usado apenas em tarefas com altos requisitos de desempenho.

O que é NDK

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.

Capacidades principais do NDK

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.

ComponenteFunção
ClangCompilador C/C++ com suporte C++20
libc++Biblioteca padrão C++ (única no NDK r27)
CMakeSistema de compilação de bibliotecas nativas
LLDBDepurador de código nativo no Android Studio
ndk-buildSistema de compilação legado (substituído pelo CMake)

NDK vs SDK: quando o código nativo é necessário

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.

Comparação SDK vs NDK por critérios

CritérioSDK (Kotlin/Java)NDK (C/C++)
DesempenhoMédio (compilação JIT/AOT)Alto (código de máquina)
Latência de áudio50–200 ms5–15 ms via Oboe
Gráficos 3DVia API Kotlin Canvas/OpenGLVulkan/OpenGL diretamente
Complexidade de desenvolvimentoBaixaAlta (gestão de memória, JNI)
Portabilidade do códigoApenas AndroidLinux, Windows, macOS, iOS
Tamanho do APKMínimo+1–10 MB por biblioteca

Arquitetura do NDK: ferramentas e bibliotecas

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.

Estrutura de diretórios do NDK

text
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

APIs nativas do Android

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: como Kotlin chama C++

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.

Declaração de uma função nativa em 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
}

// Uso no código
val bridge = NativeBridge()
println(bridge.stringFromJNI())  // "Hello from C++"

Implementação da função JNI em 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;
}

Configuração do CMakeLists.txt para NDK

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.

Exemplo de CMakeLists.txt para uma biblioteca nativa

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

Configuração do build.gradle para NDK

groovy
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 e compilação multiplataforma

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.

Determinação da ABI do dispositivo em código nativo

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");
}
ABIArquiteturaBitsDispositivos
arm64-v8aARMv8-A64 bitsQuase todos os telemóveis modernos
armeabi-v7aARMv7-A32 bitsDispositivos antigos (pré-2020)
x86_64x86-6464 bitsEmulador, Chromebook
x86x86 IA-3232 bitsEmuladores legados

Exemplos de código nativo em C++

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.

Operações matemáticas em código nativo

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

    // 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;
}

Registo de mensagens a partir de código nativo

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.

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

    LOGD("Processing complete");
}

Perguntas frequentes

É obrigatório usar NDK para desenvolvimento Android?

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.

Qual compilador o NDK usa?

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.

O que é ABI no contexto do NDK?

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.

É possível depurar código C++ através do NDK?

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.

Como o NDK afeta o tamanho do APK?

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

  • NDK — um conjunto de ferramentas para compilação cruzada de código C/C++ em bibliotecas nativas para Android usando o compilador Clang.
  • JNI — uma interface que liga Kotlin/Java ao código nativo através da palavra-chave external e convenções de nomenclatura de funções.
  • CMake — o sistema de compilação principal do NDK, configurável via CMakeLists.txt e externalNativeBuild no Gradle.
  • ABI define a arquitetura do processador — arm64-v8a, armeabi-v7a, x86_64. Cada ABI requer uma compilação separada da biblioteca.
  • Use o NDK apenas para tarefas que exijam alto desempenho: jogos, áudio, gráficos, criptografia, aprendizagem automática no dispositivo.
  • O Google Play suporta divisão por ABI através do App Bundle: o utilizador recebe apenas a biblioteca para a arquitetura do seu dispositivo.
  • As bibliotecas nativas aumentam significativamente o tamanho do APK, mas fornecem o máximo desempenho e baixa latência para operações críticas.

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.

Discutir o projeto

Leia também