Native raylib Android build pipeline running entirely inside Termux without Android Studio.
This project demonstrates how to:
- Build native raylib applications for Android
- Compile C code with the Android NDK
- Package APKs manually
- Compile Java sources manually
- Generate DEX files manually
- Link Android resources manually
- Sign APKs manually
- Work with raylib Android integration (NativeActivity) without Gradle or Android Studio
Everything is built directly inside Termux (aarch64) using shell scripts and command line tools. This project targets the Termux app from GitHub releases only (the Play Store build is deprecated and unsupported) — paths, toolchain and packaging choices below assume that environment.
This repository is intentionally minimal and low-level. Instead of relying on:
- Android Studio
- Gradle
- Java/Kotlin project generators
- raylib template generators (
Makefile.Androidcodegen)
This project exposes the actual Android native build pipeline step-by-step.
The goal is educational and practical:
- Understand how Android native packaging works
- Understand raylib Android integration (NativeActivity +
android_main) - Understand manual APK assembly
- Allow Android native development directly on-device inside Termux
This project intentionally avoids:
- Gradle
- Android Studio
- ndk-build
- Android SDK binaries (aapt2, d8, zipalign, apksigner from the SDK do not execute correctly inside Termux — the Termux-packaged equivalents are used instead)
The entire APK is assembled manually through shell scripts and command-line tooling.
- raylib + Android
- Pure C application (
main()viaandroid_native_app_glue) - Manual APK packaging
- Manual DEX generation
- Native
.soinjection - Touch input support
- Multi-touch particle demo
- No Gradle
- No Android Studio
- Works fully inside Termux from GitHub releases (aarch64-only, Play Store build unsupported)
The current example is a simple raylib particle system:
- Fullscreen window
- Multi-touch support
- Touch trails
- Randomized particles
- Gravity simulation
- Lifetime fading
Implemented in:
src/main.c
.
├── build.sh
├── clean.sh
├── gen-debug-key.sh
├── CMakeLists.txt
├── LICENSE.txt
├── README.md
├── platform/
│ └── android/
│ └── app/
│ ├── AndroidManifest.xml
│ ├── java/
│ └── res/
├── src/
│ └── main.c
├── vendor/
│ └── raylib/ (raylib checkout, not included — see below)
├── build-android/ (native CMake build, ignored)
├── build-java/ (javac classes + dex, ignored)
├── build-apk/ (reszip, base, inject, final APK, ignored)
└── build/
└── build-apk/
└── debug.keystore
This project exists specifically to prove that Android native applications can be built entirely inside Termux. Using Android Studio would defeat the purpose.
Normally Android builds are abstracted behind Gradle, Android Studio, and build plugins. This repository intentionally exposes the actual build pipeline:
- C → shared library (.so)
- Java → .class
- .class → DEX
- Resources → compiled resources
- Everything → APK
- APK → aligned
- APK → signed
This makes the Android native stack significantly easier to understand.
Several Android SDK binaries do not execute correctly inside Termux because they were built expecting a conventional Linux environment. For this reason, this project uses:
- aapt2
- d8
- zipalign
- apksigner
from the Termux repositories instead of the Android SDK versions. Java tooling is also aligned around the Termux OpenJDK environment.
The only piece required from the Android SDK itself is the platform
stub (platforms/android-34/android.jar), which contains no binaries
and is consumed as a compile/link reference. It can be installed with
the SDK command-line tools (sdkmanager "platforms;android-34").
raylib's Android backend (src/platforms/rcore_android.c) is built on
android.app.NativeActivity: the framework loads the native .so via
the android.app.lib_name manifest meta-data and calls
ANativeActivity_onCreate from android_native_app_glue, which in turn
invokes the app's plain main(). The only Java in this project is a
7-line NativeLoader extends NativeActivity trampoline (the same role
as the NativeLoader.java generated by raylib's own
examples/Makefile.Android) plus the R.java generated by aapt2.
raylib is built static (BUILD_SHARED_LIBS OFF) and linked into the
app .so, mirroring the SDL_STATIC ON choice of the sibling SDL3
project. This keeps a single native library in the APK
(lib/arm64-v8a/libraylib-android-app.so) with system deps only
(liblog, libandroid, libEGL, libGLESv2, libOpenSLES, libc,
libm, libdl) — no libc++_shared.so needed since raylib is pure C.
The Termux Android tooling ecosystem works reliably with OpenJDK 17. The build pipeline was designed around openjdk-17 inside Termux.
| Dependency | Minimum | Notes |
|---|---|---|
| Termux (aarch64) | — | $PREFIX environment, pkg packages below |
| raylib | master at time of build |
cloned into vendor/raylib (symlink to ../../raylib also works) |
| Android SDK platform | android-34 |
only platforms/android-34/android.jar is used |
| Termux-adapted NDK | r29 (aarch64) | provides toolchain + android_native_app_glue |
| openjdk-17 | 17 | javac, keytool (via pkg) |
| cmake, ninja, aapt2, d8, apksigner, zipalign, zip | — | all via pkg |
Install required packages:
pkg update
pkg install \
git \
cmake \
ninja \
openjdk-17 \
aapt2 \
d8 \
apksigner \
zipalign \
zip
The build expects two environment variables pointing wherever you installed each piece (no fixed location required):
ANDROID_HOME— an Android SDK whoseplatforms/android-34/holdsandroid.jar(only this stub is used from the SDK). Install it with the SDK command-line tools, e.g.:
sdkmanager "platforms;android-34"ANDROID_NDK_HOME— the extracted Termux-adapted NDK root (the directory containingbuild/cmake/android.toolchain.cmake).
export ANDROID_HOME=<your-sdk-dir>
export ANDROID_NDK_HOME=<your-ndk-dir>Only the platform stub is used from the SDK — packaging tools (aapt2, d8, zipalign, apksigner) come from the Termux packages above.
This project uses termux-ndk.
The official Android NDK does not integrate cleanly with Termux in many cases. The Termux-adapted NDK avoids several compatibility issues.
Download the Termux-adapted NDK tarball (do NOT clone the repo — it does not contain a usable NDK tree) and extract it to a directory of your choice:
mkdir -p <ndk-parent-dir>
curl -sL https://github.com/lzhiyong/termux-ndk/releases/download/android-ndk/android-ndk-r29-aarch64.tar.xz \
| tar -xJ -C <ndk-parent-dir>Then set ANDROID_NDK_HOME to the extracted NDK root — the
directory containing build/cmake/android.toolchain.cmake (check
with ls, as the tarball creates a versioned subdirectory).
raylib is intentionally not included in this repository.
Clone raylib into vendor/raylib:
git clone --depth=1 https://github.com/raysan5/raylib.git vendor/raylib(A sibling checkout symlinked as vendor/raylib -> ../../raylib also works.)
This repository expects:
vendor/raylib/src/raylib.h
vendor/raylib/src/platforms/rcore_android.c
Official raylib repository: https://github.com/raysan5/raylib
Before building:
chmod +x gen-debug-key.sh
./gen-debug-key.sh
This generates: build/build-apk/debug.keystore
chmod +x build.sh
./build.sh
Final APK output:
build-apk/final/app-signed.apk
Intermediate outputs: build-android/ (native .so),
build-java/ (classes, dex), build-apk/ (reszip,
base, inject, final).
chmod +x clean.sh
./clean.shThe build script performs the following steps:
- Configure CMake for Android (
PLATFORM=Android, raylib static) - Compile the app
.sowith the NDK glue baked in (android_main,-u ANativeActivity_onCreate) - Compile Java sources (
NativeLoadertrampoline) - Generate DEX bytecode
- Compile Android resources
- Link the base APK
- Inject native
.so(libraylib-android-app.so) andclasses.dex - Align APK
- Sign APK
MIT — see LICENSE.txt. raylib itself is zlib/libpng licensed; see vendor/raylib/LICENSE.