Crate android_activity

source ·
Expand description

A glue layer for building standalone, Rust applications on Android

This crate provides a “glue” layer for building native Rust applications on Android, supporting multiple Activity base classes. It’s comparable to android_native_app_glue.c for C/C++ applications.

Currently the crate supports two Activity base classes:

  1. NativeActivity - Built in to Android, this doesn’t require compiling any Java or Kotlin code.
  2. GameActivity - From the Android Game Development Kit, it has more sophisticated input handling support than NativeActivity. GameActivity is also based on the AndroidAppCompat class which can help with supporting a wider range of devices.

Standalone applications based on this crate need to be built as cdylib libraries, like:


and implement a #[no_mangle] android_main entry point like this:

fn android_main(app: AndroidApp) {


Once your application’s Activity class has loaded and it calls onCreate then android-activity will spawn a dedicated thread to run your android_main function, separate from the Java thread that created the corresponding Activity.

AndroidApp provides an interface to query state for the application as well as monitor events, such as lifecycle and input events, that are marshalled between the Java thread that owns the Activity and the native thread that runs the android_main() code.

§Cheaply Clonable AndroidApp

AndroidApp is intended to be something that can be cheaply passed around by referenced within an application. It is reference counted and can be cheaply cloned.

§Send and Sync AndroidApp

Although an AndroidApp implements Send and Sync you do need to take into consideration that some APIs, such as AndroidApp::poll_events() are explicitly documented to only be usable from your android_main() thread.

§Main Thread Initialization

Before android_main() is called, the following application state is also initialized:

  1. An I/O thread is spawned that will handle redirecting standard input and output to the Android log, visible via logcat.
  2. A JavaVM and Activity instance will be associated with the ndk_context crate so that other, independent, Rust crates are able to find a JavaVM for making JNI calls.
  3. The JavaVM will be attached to the native thread
  4. A Looper is attached to the Rust native thread.

These are undone after android_main() returns

§Android Extensible Enums

There are numerous enums in the android-activity API which are effectively bindings to enums declared in the Android SDK which need to be considered runtime extensible.

Any enum variants that come from the Android SDK may be extended in future versions of Android and your code could be exposed to new variants if you build an application that might be installed on new versions of Android.

This crate follows a convention of adding a hidden __Unknown(u32) variant to these enum to ensure we can always do lossless conversions between the integers from the SDK and our corresponding Rust enums. This can be important in case you need to pass certain variants back to the SDK regardless of whether you knew about that variants specific semantics at compile time.

You should never include this __Unknown(u32) variant within any exhaustive pattern match and should instead treat the enums like #[non_exhaustive] enums that require you to add a catch-all for any unknown => {} values.

Any code that would exhaustively include the __Unknown(u32) variant when pattern matching can not be guaranteed to be forwards compatible with new releases of android-activity which may add new Rust variants to these enums without requiring a breaking semver bump.

You can (infallibly) convert these enums to and from primitive u32 values using .into():

For example, here is how you could ensure forwards compatibility with both compile-time and runtime extensions of a SomeEnum enum:

match some_enum {
    SomeEnum::Foo => {},
    SomeEnum::Bar => {},
    unhandled => {
        let sdk_val: u32 = unhandled.into();
        println!("Unhandled enum variant {some_enum:?} has SDK value: {sdk_val}");