Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bevy_android_jni

Following released Bevy versions crates.io docs.rs discord

The JNI preamble every Bevy Android integration needs exactly once: reach the process JavaVM and the activity that android-activity holds, and load Java classes out of a dex embedded in your crate.

This is the shared plumbing under our bevy_android_* crates (safe area, haptics, alerts, review, …).

Features

  • with_activity — run a closure with a JNI Env attached to the current thread and the activity object, from any Bevy system
  • load_embedded_class — turn include_bytes!-embedded dex bytes into a loaded class via InMemoryDexClassLoader (API 26+), parented to the activity's class loader
  • empty off Android: depend on it target-gated and keep your own crate free of stray dependencies on other platforms

Instructions

Add the dependency, gated to Android next to jni itself:

[target.'cfg(target_os = "android")'.dependencies]
bevy_android_jni = "0.1"
jni = "0.22"

Call into the platform from any system (requires an app entry annotated with Bevy's #[bevy_main], which populates ANDROID_APP):

use bevy_android_jni::with_activity;

fn window_token() {
    let _ = with_activity(|env, activity| {
        let window = env.call_method(
            activity,
            jni::jni_str!("getWindow"),
            jni::jni_sig!("()Landroid/view/Window;"),
            &[],
        )?;
        // ... any JNI against the activity
        Ok(())
    });
}

Shipping Java code in a plain Rust crate

If your integration needs a Java class (a dialog builder, a listener, …), you do not need Gradle source sets, an AAR, or vendored .java files in the consuming app. Compile the class to a dex in build.rs (see the android-build crate for javac/d8 wrappers), embed it, and load it:

use std::sync::OnceLock;
use bevy_android_jni::{load_embedded_class, with_activity};
use jni::{jni_str, objects::JClass, refs::Global};

static DEX: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/my_crate.dex"));
static CLASS: OnceLock<Global<JClass<'static>>> = OnceLock::new();

fn bridge_class() {
    let _ = with_activity(|env, activity| {
        let class = load_embedded_class(
            env,
            activity,
            DEX,
            jni_str!("com/example/mycrate/Bridge"),
        )?;
        // Register `native` methods explicitly: symbol-name resolution is not
        // defined for classes from runtime-created loaders. jni's
        // `native_method!` macro produces the checked `NativeMethod` entries.
        // SAFETY: signatures are compile-time checked by `native_method!`.
        // unsafe { env.register_native_methods(&class, &[ON_RESPONSE]) }?;
        let _ = CLASS.set(env.new_global_ref(&class)?);
        Ok(())
    });
}

The loader created for a dex is cached for the process, so every class from one dex shares its defining loader (one Class identity, natives registered once). InMemoryDexClassLoader needs API 26+ (Android 8.0).

Our Other Crates

Bevy version support

bevy crate
0.19 0.1,main

License

All code in this repository is dual-licensed under either:

at your option. This means you can select the license you prefer.

Your contributions

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages