A modern, high-performance starter template for integrating Rust into Android applications using Cargo Workspaces, JNI (v0.22+), and cargo-ndk automated Gradle tasks.
- ⚡ Modular Cargo Workspace: Clean separation between pure Rust business logic (
core_engine) and Android FFI bindings (android_binding). - 🤖 Automated Gradle Build: Native libraries (
.so) are automatically compiled viacargo-ndkdirectly during the Android build lifecycle. - 🔒 Safe & Modern JNI Integration: Built with
jnicrate v0.22+, utilizingEnvUnownedfor thread-safe native execution. - 📱 Multi-Architecture Support: Pre-configured target ABIs for
arm64-v8a,armeabi-v7a, andx86_64. - 🧹 Clean Git Tracking: Hierarchical
.gitignoresetups ensuring native binaries,target/builds, and IDE caches stay out of your repository.
AndroidRustStarter/
├── app/ # Android Application Module (Kotlin / Gradle)
│ ├── build.gradle.kts # Contains `buildRust` task integrated into Gradle build
│ └── src/main/
│ ├── java/org/rust/starter/
│ │ └── NativeBridge.kt # Kotlin JNI Bridge
│ └── jniLibs/ # Output folder for compiled Rust .so files (Ignored in Git)
│
└── rust/ # Cargo Workspace Root
├── Cargo.toml # Workspace definition (resolver = "3")
├── core_engine/ # Pure Rust logic (Math, Crypto, Business Logic)
└── android_binding/ # FFI Layer (JNI functions exported to Kotlin)
- Android Studio with Android NDK (Side by side) installed via SDK Manager.
- Rust Toolchain: Install via rustup.rs.
- Android Targets for Rust:
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
cargo-ndk:cargo install cargo-ndk
git clone [https://github.com/sandri-kun/Android-Rust-Starter.git](https://github.com/sandri-kun/Android-Rust-Starter.git)
cd Android-Rust-Startercd rust
cargo check- Open the root folder
AndroidRustStarterin Android Studio. - Switch Project View:
How to view the Rust Workspace: In the top-left Project pane, change the view mode from Android to Project. The default Android view filters out non-Android folders, so the
rust/workspace will only be visible when set to Project view. - Ensure your NDK path is detected in
local.properties. - Select the
appconfiguration in the toolbar and hit Run (Shift + F10).
💡 The Gradle task
buildRustwill automatically executecargo ndkto compile the native libraries intoapp/src/main/jniLibs/before APK packaging.
Instead of relying on unmaintained Gradle plugins, this project uses a custom Exec task in app/build.gradle.kts:
val buildRust = tasks.register<Exec>("buildRust") {
description = "Compile Rust library for Android targets using cargo-ndk"
workingDir = file("../rust")
val isWindows = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
.getCurrentOperatingSystem().isWindows
val executableName = if (isWindows) "cargo.exe" else "cargo"
commandLine(
executableName, "ndk",
"-t", "arm64-v8a",
"-t", "armeabi-v7a",
"-t", "x86_64",
"-o", "${projectDir}/src/main/jniLibs",
"build",
"--release"
)
}
tasks.whenTaskAdded {
if (name.startsWith("merge") && name.endsWith("JniLibFolders")) {
dependsOn(buildRust)
}
}use jni::EnvUnowned;
use jni::objects::{JClass, JString};
use jni::sys::{jint, jstring};
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_rust_starter_NativeBridge_add<'local>(
_unowned_env: EnvUnowned<'local>,
_class: JClass<'local>,
a: i32,
b: i32,
) -> jint {
a + b
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_org_rust_starter_NativeBridge_greet<'local>(
mut unowned_env: EnvUnowned<'local>,
_class: JClass<'local>,
name: JString<'local>,
) -> jstring {
let outcome = unowned_env.with_env(|env| {
#[allow(deprecated)]
let java_str = env.get_string(&name).expect("Failed to read JNI String");
let name_str: String = java_str.into();
let greeting = format!("Hello {}, greetings from Rust!", name_str);
let output_jstring = env.new_string(greeting).expect("Failed to create JString");
Ok::<jstring, jni::errors::Error>(output_jstring.into_raw())
});
match outcome.outcome() {
Ok(raw_jstring) => raw_jstring,
Err(_) => std::ptr::null_mut(),
}
}package org.rust.starter
object NativeBridge {
init {
System.loadLibrary("android_binding")
}
external fun add(a: Int, b: Int): Int
external fun greet(name: String): String
}Contributions, issues, and feature requests are welcome! Feel free to check the issues page.
Distributed under the MIT License. See LICENSE for more information.