Tutorial 82: Android Build and Deployment
What you’ll learn
- Setting up the NDK and the SDL3 Android project template.
- Wiring CNA into the Gradle/CMake build for an ARM64-v8a target.
- Running on OpenGL ES 3.0, and packaging the APK.
- Where Android support genuinely stands, and what is untested.
Before you start — Tutorial 80: Cross-Platform Build Guide (the cross-platform build baseline), Tutorial 49: Touch Input and Gestures and Tutorial 50: Accelerometer and Sensors (the input paths a phone actually uses).
Where Android support actually stands. The code paths and NDK sensor backends exist and the build wiring is real, but no CI workflow builds or runs anything on Android. Everything on this page is a documented route, not a machine-verified one — budget time for hardware testing and expect to be the person who finds the problems.
Two concrete gaps to plan around. Video is absent entirely on Android: CNA's video translation units are excluded from the build, so Video and VideoPlayer are missing symbols — calling code compiles and then fails to link. The tag selects SDL_RENDERER by default on Android, but has no automatic Android build or runtime lane; do not generalize source wiring into per-device graphics evidence.
Android NDK setup
# Install an NDK through Android Studio's SDK Manager, then point at that exact install.
# Alpha.1's checked-in Android notes record NDK 29/30-era toolchains; do not assume a path.
export ANDROID_NDK=/absolute/path/to/Android/Sdk/ndk/your-installed-version
SDL3 Android project template
SDL3 provides a complete Android project template in SDL/android-project/. Copy it and add
your CNA sources to the JNI layer:
cp -r $CNA_ROOT/third_party/SDL/android-project ./MyAndroidGame
cd MyAndroidGame
# Add your C++ sources to app/jni/src/
CNA CMake integration
# app/jni/CMakeLists.txt — follow alpha.1's checked-in Devices APK shape.
cmake_minimum_required(VERSION 3.20)
project(MyGame LANGUAGES CXX)
set(CNA_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(CNA_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(CNA_GRAPHICS_RENDERER SDL_RENDERER CACHE STRING "" FORCE)
add_subdirectory(${CNA_SOURCE_DIR} cna-build)
# SDLActivity loads a shared object named main.
add_library(main SHARED main.cpp)
target_compile_features(main PRIVATE cxx_std_23)
target_link_libraries(main PRIVATE CNA SHARP_RUNTIME SDL3::SDL3)
CNA_SOURCE_DIR above is an application-local path variable pointing at the alpha.1 source checkout; it is not a CNA cache option. CNA's own packaged example nests this pattern under modules/devices/examples/demo_devices/android/. A copied SDL template still needs matching Gradle, manifest and SDLActivity wiring.
ARM64-v8a target
Build for arm64-v8a (AArch64) as the primary Android ABI. Add another ABI only after
validating CNA and all native dependencies for it; alpha.1 does not provide an Android CI matrix.
cmake -S . -B build-android \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-24 \
-DCNA_BUILD_TESTS=OFF \
-DCNA_BUILD_EXAMPLES=OFF \
-DCNA_GRAPHICS_RENDERER=SDL_RENDERER
OpenGL ES 3.0 on Android
Alpha.1 defaults Android to SDL_RENDERER. If your separately validated application chooses
OPENGLES3, it also needs the EasyGL/meta-gl sibling checkouts and a device that advertises
OpenGL ES 3.0. Declare that requirement in AndroidManifest.xml so package tooling can filter
incompatible devices:
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
TouchPanel (needs hardware validation)
CNA exposes TouchPanel for multi-touch input on Android. The API mirrors XNA's
TouchCollection but requires a real device for proper validation — the emulator's touch
simulation is unreliable for multi-finger gestures.
auto touches = TouchPanel::GetState();
for (auto& loc : touches) {
// loc.Position is in screen pixels, loc.State is Pressed/Moved/Released
HandleTouch(loc.Position, loc.State);
}
Accelerometer on Android
Accelerometer and Gyroscope are not Android-only. Both reach a real SDL3 hardware probe on desktop Linux, Windows and macOS too, so you can develop against them without a phone in hand. Compass and Motion are Android-only. See Tutorial 50.
SDL3 exposes the device accelerometer through sensor events. CNA wraps this in the
Accelerometer class, mirroring the XNA API.
#include "Microsoft/Devices/Sensors/Accelerometer.hpp"
Accelerometer::IsSupported(); // true on Android with a real sensor
AccelerometerReading reading = Accelerometer::GetCurrentReading();
// reading.Acceleration is a Vector3 in m/s²
// X: left/right tilt, Y: forward/back tilt, Z: gravity (~9.8 face-up)
APK packaging
# Build debug APK
cd MyAndroidGame
./gradlew assembleDebug
# Install on connected device
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n com.example.mygame/.MainActivity
# Build release APK (requires signing key)
./gradlew assembleRelease
Gradle configuration
// app/build.gradle
android {
compileSdk 34
defaultConfig {
applicationId "com.example.mygame"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
externalNativeBuild {
cmake {
abiFilters "arm64-v8a", "armeabi-v7a"
arguments "-DCNA_GRAPHICS_RENDERER=OPENGLES3"
cppFlags "-std=c++23"
}
}
}
externalNativeBuild {
cmake {
path "jni/CMakeLists.txt"
version "3.20.0"
}
}
}