This example shows off how to use rust to build a native library from Android and use it through an automatically generated JNI wrapper.

Android Studio can be used to work with Rust via its Rust plugin. So it's not a bad idea to integrate invocations of cargo into gradle, so you can build and run Rust inside an ordinary Java/Kotlin Android application.

Project Structure

The file defines how flapigen generates wrapper code:

    let swig_gen = flapigen::Generator::new(LanguageConfig::JavaConfig(

The file src/ contains real code that will be invoked from Java:

// src/
struct Session {
    a: i32,

impl Session {
    pub fn new() -> Session {
        #[cfg(target_os = "android")]
        log_panics::init(); // log panics rather than printing them
        info!("init log system - done");
        Session { a: 2 }

    pub fn add_and1(&self, val: i32) -> i32 {
        self.a + val + 1

    // Greeting with full, no-runtime-cost support for newlines and UTF-8
    pub fn greet(to: &str) -> String {
        format!("Hello {} ‚úč\nIt's a pleasure to meet you!", to)

And the file src/ contains descriptions for flapigen to export this API to Java:

// src/
foreign_class!(class Session {
    self_type Session;
    constructor Session::new() -> Session;
    fn Session::add_and1(&self, val: i32) -> i32;
    fn Session::greet(to: &str) -> String;

Then the app/build.gradle contains rules to invoke cargo to build a shared library from Rust code, and then build it into apk:

// app/build.gradle
def rustBasePath = ".."
def archTriplets = [
    'armeabi-v7a': 'armv7-linux-androideabi',
    'arm64-v8a': 'aarch64-linux-android',

// TODO: only pass --release if buildType is release
archTriplets.each { arch, target ->
    // execute cargo metadata and get path to target directory
    tasks.create(name: "cargo-output-dir-${arch}", description: "Get cargo metadata") {
        new ByteArrayOutputStream().withStream { os ->
            exec {
                commandLine 'cargo', 'metadata', '--format-version', '1'
                workingDir rustBasePath
                standardOutput = os
            def outputAsString = os.toString()
            def json = new groovy.json.JsonSlurper().parseText(outputAsString)

  "cargo target directory: ${json.target_directory}")
            project.ext.cargo_target_directory = json.target_directory
    // Build with cargo
    tasks.create(name: "cargo-build-${arch}", type: Exec, description: "Building core for ${arch}", dependsOn: "cargo-output-dir-${arch}") {
        workingDir rustBasePath
        commandLine 'cargo', 'build', "--target=${target}", '--release'
    // Sync shared native dependencies
    tasks.create(name: "sync-rust-deps-${arch}", type: Sync, dependsOn: "cargo-build-${arch}") {
        from "${rustBasePath}/src/libs/${arch}"
        include "*.so"
        into "src/main/libs/${arch}"
    // Copy build libs into this app's libs directory
    tasks.create(name: "rust-deploy-${arch}", type: Copy, dependsOn: "sync-rust-deps-${arch}", description: "Copy rust libs for (${arch}) to jniLibs") {
        from "${project.ext.cargo_target_directory}/${target}/release"
        include "*.so"
        into "src/main/libs/${arch}"

    // Hook up tasks to execute before building java
    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn "rust-deploy-${arch}"
    preBuild.dependsOn "rust-deploy-${arch}"

    // Hook up clean tasks
    tasks.create(name: "clean-${arch}", type: Delete, description: "Deleting built libs for ${arch}", dependsOn: "cargo-output-dir-${arch}") {
        delete fileTree("${project.ext.cargo_target_directory}/${target}/release") {
            include '*.so'
    clean.dependsOn "clean-${arch}"


To build the demo, you will need the latest version of Cargo, Android NDK and install the proper Rust toolchain targets:

rustup target add arm-linux-androideabi
rustup target add aarch64-linux-android

To link Rust code into a shared library you need add the path to the proper clang binary into your PATH environment variable or change the path to the linker here:

linker = "aarch64-linux-android21-clang++"
runner = "./"

linker = "armv7a-linux-androideabi21-clang++"
runner = "./"


Gradle will take care of building and deploying the Rust sources. Thus, to build the project in release mode, simply call ./gradlew androidRelease.

To build only the rust libraries for a specific target, call cargo as usual, e.g. cargo build --target arm-linux-androideabi.


It is possible to run Rust unit tests on Android phone via script mentioned in .cargo/config, there are also instrumentation unit tests in Java that invoke Rust code.