Tutorial 82: Android Build and Deployment

CNA — C++ XNA 4.0 reimplementation

Android NDK setup

# Install via Android Studio SDK Manager, or command-line:
sdkmanager "ndk;26.1.10909125"
export ANDROID_NDK=$HOME/Android/Sdk/ndk/26.1.10909125

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
cmake_minimum_required(VERSION 3.20)
project(MyGame CXX)
set(CMAKE_CXX_STANDARD 23)

# SDL3 is provided by the Android project template
find_library(SDL3_LIB SDL3 PATHS ${SDL3_DIR}/lib/${ANDROID_ABI})

add_library(MyGame SHARED
    main.cpp
    # your other sources
)

target_include_directories(MyGame PRIVATE
    ${CNA_INCLUDE_DIR}
    ${SDL3_INCLUDE_DIR}
)

target_link_libraries(MyGame PRIVATE
    ${SDL3_LIB}
    android
    log
    EGL
    GLESv3
)

ARM64-v8a target

Build for arm64-v8a (AArch64) as the primary Android ABI. This covers all Android devices from 2014 onward. Add armeabi-v7a for older 32-bit devices if needed.

cmake -S . -B build-android \
  -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
  -DANDROID_ABI=arm64-v8a \
  -DANDROID_PLATFORM=android-24 \
  -DCNA_GRAPHICS_BACKEND=EASYGL

OpenGL ES 3.0 on Android

All Android devices from API level 18+ support OpenGL ES 3.0. CNA's EASYGL backend targets GLES 3.0 which matches perfectly. Declare the requirement in AndroidManifest.xml so the Play Store filters out 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

SDL3 exposes the device accelerometer through sensor events. CNA wraps this in the Accelerometer class, mirroring the XNA API.

#include "Microsoft/Xna/Framework/Input/Accelerometer.hpp"

Accelerometer::IsSupported(); // returns true on Android with 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_BACKEND=EASYGL"
                cppFlags "-std=c++23"
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "jni/CMakeLists.txt"
            version "3.20.0"
        }
    }
}