My Quick Notes

Ain't Nobody Got Time to Read

April 22, 2025

How to properly exit Bevy app on Android

Normally, a Bevy application can exit with AppExit, like:

fn exit_system(mut exit_event: MessageWriter<AppExit>) {
    exit_event.write(AppExit::Success);
}

But on Android, that is not enough and can cause the app fails to reopen. To completely exit the Bevy application on Android, we need to exit on the Android side too.

 .--------------------------------------.           .------------------------------.
|                  Bevy                  |         |            Android             |
|   .------------.     .------------.    |         |   .------------.               |
|  |    system    |   |     main     |   |         |  | MainActivity |              |
|   '-----+-+----'     '-----+-+----'    |         |   '-----+-+----'               |
|         | |    AppExit     | |         |         |         | |                    |
|         +-+--------------->| |         |         |         | |                    |
|                            | |     exitAppActivity         | |                    |
|                            +-+---------------------------->| |                    |
|                                        |         |         | +---.                |
|                                        |         |         | |   | finishAffinity |
|                                        |         |         | |   |                |
|                                        |         |         +-+<--'                |
|                                        |         |                                |
 '--------------------------------------'           '------------------------------'

Start with the Android example: https://github.com/bevyengine/bevy/tree/main/examples/mobile/android_example

Java wrapper

In MainActivity.java, add a function for exit the app, then Bevy app can call it in the Rust side:

...
import android.os.Handler;


public class MainActivity extends GameActivity {
...

    // This function will exit the Android app
    public void exitAppActivity() {
        // `finishAffinity` needs to be run in main thread
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                MainActivity.this.finishAffinity();
            }
        });
    }
}

Bevy app

Add dependency jni for android target in Cargo.toml.

[target.'cfg(target_os = "android")'.dependencies]
jni = "0.21"

In Bevy app, call the Android function exitAppActivity after the app.run() has finished:

#[bevy_main]
fn main() {
    let mut app = App::new();
    ...
    app.run();

    // Exit the Android app after Bevy job is finished
    #[cfg(target_os = "android")]
    exit_android_app();
}

#[cfg(target_os = "android")]
fn exit_android_app() -> anyhow::Result<()> {
    let ctx = bevy::android::ANDROID_APP.get().expect("ANDROID_APP should be defined");
    let vm = unsafe { jni::JavaVM::from_raw(ctx.vm_as_ptr() as *mut *const jni::sys::JNIInvokeInterface_) }?;
    let activity = unsafe { jni::objects::JObject::from_raw(ctx.activity_as_ptr() as *mut jni::sys::_jobject) };
    let mut env = vm.attach_current_thread()?;

    // Call MainActivity::exitAppActivity
    env.call_method(activity, "exitAppActivity", "()V", &[])?;
    Ok(())
}
PreviousVim - Save file as root without reopening file
NextBuckle spring keyboard sound on Fedora Wayland

Comments