Как разрабатывать Android‑приложения на C/C++ в 2026

Короткий ответ: в 2026 для высокопроизводительных модулей используют NDK r27+ (compileSdk 36) и связку Gradle + CMake; альтернативой C++ становится Rust через cargo‑ndk. Ниже — практическая настройка, пример и рекомендации для запуска нативного кода в Kotlin/Compose.

Быстрая настройка и интеграция NDK с SDK

  1. Установите NDK и инструменты:
  • sdkmanager "ndk;27.0.12077973"
  • Убедитесь, что у вас Android Gradle Plugin и Gradle 8.7+.
  1. В модуле app (build.gradle.kts) укажите:
android {
  compileSdk = 36
  ndkVersion = "27.0.12077973"
  externalNativeBuild {
    cmake {
      path = "src/main/cpp/CMakeLists.txt"
      version = "3.28"
    }
  }
  defaultConfig {
    externalNativeBuild {
      cmake { cppFlags = "-std=c++17" }
    }
    ndk {
      abiFilters += listOf("arm64-v8a","armeabi-v7a","x86_64")
    }
  }
}
  1. Простой C++ JNI‑функция (src/main/cpp/native-lib.cpp):
#include <jni.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapp_MainActivity_stringFromJNI(JNIEnv* env, jobject) {
  return env->NewStringUTF("Hello from C++ NDK 2026!");
}

Kotlin:

class MainActivity : ComponentActivity() {
  private external fun stringFromJNI(): String
  companion object { init { System.loadLibrary("native-lib") } }
}
  1. Сборка: ./gradlew assembleDebug — в APK появится libnative-lib.so для указанных ABI.

Если планируете безопасную логику — рассмотрите Rust через cargo-ndk: он генерирует .so и сокращает риски ошибок памяти.

Пример проекта: OpenGL‑рендерер в Compose

Структура: app/src/main/cpp/{CMakeLists.txt, gl-renderer.cpp, gl-renderer.h} Основные шаги:

  • CMake: подключите GLESv3 и log.
cmake_minimum_required(VERSION 3.28)
add_library(gl-renderer SHARED gl-renderer.cpp)
find_library(log-lib log)
target_link_libraries(gl-renderer ${log-lib} GLESv3 EGL)
  • В gl-renderer.cpp реализуйте JNI‑методы init(width,height), drawFrame().
  • В Kotlin используйте AndroidView/GlSurfaceView или NativeComposeInterop для рендера в Compose, создайте хук на lifecycle для вызова native init/draw.

Реальные оптимизации: используйте precompiled shaders, VBO/VAO, минимизируйте переключения state, профилируйте GPU.

Не создавайте std::thread внутри UI‑потока без синхронизации. Для JNI‑вызовов предпочитайте корутины, HandlerThread или native task queues.

Сравнение C++ и Rust (когда выбирать)

  • C++: максимум экосистемы (игры, существующий код), оптимизации LLVM.
  • Rust: почти та же производительность, значительно выше безопасность памяти — хорош для новых ML/приложений и критичных модулей.
  • Выбор: перенос legacy/Unity/Unreal — C++; новая критичная логика — Rust.

Частые ошибки

  • Неправильный ndkVersion в Gradle → сборка использует другой NDK.
  • Забыт System.loadLibrary(...) или несоответствие имени библиотеки.
  • Выставлены неправильные abiFilters — отсутствуют нужные .so в APK.
  • Использование blocking IO/threads в UI без синхронизации → ANR/краши.
  • Несоответствие compileSdk и используемых фич (Compose NativeInterop требует compileSdk 36+).

FAQ

  • Нужно ли использовать NDK для простых приложений? Нет — только для критичных по производительности модулей.
  • Как тестировать нативный код? Используйте Android Studio Profiler + perfetto и аппаратные профайлеры; эмулятор полезен, но тестируйте на реальных устройствах с нужной архитектурой.
  • Можно ли смешивать C++ и Rust в одном модуле? Да — Rust может экспортировать C ABI, затем подключать через C++/JNI.

Лучшие практики: профилируйте CPU/GPU, минимизируйте переходы между Java/Kotlin и native, покрывайте ключевые нативные участки тестами, автоматизируйте сборку через AGP + CMake/cargo‑ndk.