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, …).
with_activity— run a closure with a JNIEnvattached to the current thread and the activity object, from any Bevy systemload_embedded_class— turninclude_bytes!-embedded dex bytes into a loaded class viaInMemoryDexClassLoader(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
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(())
});
}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).
- bevy_debug_log
- bevy_device_lang
- bevy_web_popups
- bevy_libgdx_atlas
- bevy_ios_iap
- bevy_ios_review
- bevy_ios_gamecenter
- bevy_ios_alerts
- bevy_ios_notifications
- bevy_ios_impact
- bevy_ios_safearea
| bevy | crate |
|---|---|
| 0.19 | 0.1,main |
All code in this repository is dual-licensed under either:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option. This means you can select the license you prefer.
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.