mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-04 05:47:26 +00:00
Improve docs
This removes the indirection of the `StateSaver` and `StateLoader` type aliases (which resulted in no documentation for these interfaces) and we now simply re-export the types from the backend implementation.
This commit is contained in:
@@ -64,6 +64,7 @@ fn android_main(app: AndroidApp) {
|
||||
|
||||
app.input_events(|event| {
|
||||
info!("Input Event: {event:?}");
|
||||
InputStatus::Unhandled
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,13 @@ use ndk::configuration::{
|
||||
ScreenSize, Touchscreen, UiModeNight, UiModeType,
|
||||
};
|
||||
|
||||
/// A (cheaply clonable) reference to this application's [`ndk::configuration::Configuration`]
|
||||
///
|
||||
/// This provides a thread-safe way to access the latest configuration state for
|
||||
/// an application without deeply copying the large [`ndk::configuration::Configuration`] struct.
|
||||
///
|
||||
/// If the application is notified of configuration changes then those changes
|
||||
/// will become visible via pre-existing configuration references.
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigurationRef {
|
||||
config: Arc<RwLock<Configuration>>,
|
||||
|
||||
@@ -655,9 +655,6 @@ pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) {
|
||||
// code to look up non-standard Java classes.
|
||||
android_main(app);
|
||||
|
||||
// Since this is a newly spawned thread then the JVM hasn't been attached
|
||||
// to the thread yet. Attach before calling the applications main function
|
||||
// so they can safely make JNI calls
|
||||
if let Some(detach_current_thread) = (*(*jvm)).DetachCurrentThread {
|
||||
detach_current_thread(jvm);
|
||||
}
|
||||
|
||||
+82
-10
@@ -1,3 +1,62 @@
|
||||
//! 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`][ndk_concepts]
|
||||
//! 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:
|
||||
//! ```
|
||||
//! [lib]
|
||||
//! crate_type=["cdylib"]
|
||||
//! ```
|
||||
//!
|
||||
//! and implement a `#[no_mangle]` `android_main` entry point like this:
|
||||
//! ```rust
|
||||
//! #[no_mangle]
|
||||
//! 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.
|
||||
//!
|
||||
//! # 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
|
||||
//!
|
||||
//! [`Activity`]: https://developer.android.com/reference/android/app/Activity
|
||||
//! [`NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity
|
||||
//! [ndk_concepts]: https://developer.android.com/ndk/guides/concepts#naa
|
||||
//! [`GameActivity`]: https://developer.android.com/games/agdk/integrate-game-activity
|
||||
//! [Looper]: https://developer.android.com/reference/android/os/Looper
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
@@ -54,13 +113,7 @@ pub use config::ConfigurationRef;
|
||||
|
||||
mod util;
|
||||
|
||||
// Note: unlike in ndk-glue this has signed components (consistent
|
||||
// with Android's ARect) which generally allows for representing
|
||||
// rectangles with a negative/off-screen origin. Even though this
|
||||
// is currently just used to represent the content rect (that probably
|
||||
// wouldn't have any negative components) we keep the generality
|
||||
// since this is a primitive type that could potentially be used
|
||||
// for more things in the future.
|
||||
/// A rectangle with integer edge coordinates. Used to represent window insets, for example.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Rect {
|
||||
pub left: i32,
|
||||
@@ -103,9 +156,11 @@ impl From<ndk_sys::ARect> for Rect {
|
||||
}
|
||||
}
|
||||
|
||||
pub type StateSaver<'a> = activity_impl::StateSaver<'a>;
|
||||
pub type StateLoader<'a> = activity_impl::StateLoader<'a>;
|
||||
pub use activity_impl::StateSaver;
|
||||
pub use activity_impl::StateLoader;
|
||||
|
||||
|
||||
/// An application event delivered during [`AndroidApp::poll_events`]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug)]
|
||||
pub enum MainEvent<'a> {
|
||||
@@ -197,6 +252,7 @@ pub enum MainEvent<'a> {
|
||||
InsetsChanged {},
|
||||
}
|
||||
|
||||
/// An event delivered during [`AndroidApp::poll_events`]
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum PollEvent<'a> {
|
||||
@@ -205,6 +261,9 @@ pub enum PollEvent<'a> {
|
||||
Main(MainEvent<'a>),
|
||||
}
|
||||
|
||||
/// Indicates whether an application has handled or ignored an event
|
||||
///
|
||||
/// If an event is not handled by an application then some default handling may happen.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum InputStatus {
|
||||
Handled,
|
||||
@@ -380,6 +439,13 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
/// The top-level state and interface for a native Rust application
|
||||
///
|
||||
/// `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.
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AndroidApp {
|
||||
pub(crate) inner: Arc<RwLock<AndroidAppInner>>,
|
||||
@@ -398,6 +464,7 @@ impl Hash for AndroidApp {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl AndroidApp {
|
||||
/// Queries the current [`NativeWindow`] for the application.
|
||||
///
|
||||
@@ -408,7 +475,7 @@ impl AndroidApp {
|
||||
self.inner.read().unwrap().native_window()
|
||||
}
|
||||
|
||||
/// Polls for any events associated with this AndroidApp and processes those events
|
||||
/// Polls for any events associated with this [AndroidApp] and processes those events
|
||||
/// (such as lifecycle events) via the given `callback`.
|
||||
///
|
||||
/// It's important to use this API for polling, and not call [`ALooper_pollAll`] directly since
|
||||
@@ -420,6 +487,11 @@ impl AndroidApp {
|
||||
/// main thread. The [`MainEvent::SaveState`] event is also synchronized with the
|
||||
/// Java main thread.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This must only be called from your `android_main()` thread and it may panic if called
|
||||
/// from another thread.
|
||||
///
|
||||
/// [`ALooper_pollAll`]: ndk::looper::ThreadLooper::poll_all
|
||||
pub fn poll_events<F>(&self, timeout: Option<Duration>, callback: F)
|
||||
where
|
||||
|
||||
@@ -816,9 +816,6 @@ extern "C" fn ANativeActivity_onCreate(
|
||||
// code to look up non-standard Java classes.
|
||||
android_main(app);
|
||||
|
||||
// Since this is a newly spawned thread then the JVM hasn't been attached
|
||||
// to the thread yet. Attach before calling the applications main function
|
||||
// so they can safely make JNI calls
|
||||
if let Some(detach_current_thread) = (*(*jvm)).DetachCurrentThread {
|
||||
detach_current_thread(jvm);
|
||||
}
|
||||
|
||||
@@ -42,30 +42,37 @@ pub const LOOPER_ID_MAIN: libc::c_int = 1;
|
||||
pub const LOOPER_ID_INPUT: libc::c_int = 2;
|
||||
//pub const LOOPER_ID_USER: ::std::os::raw::c_uint = 3;
|
||||
|
||||
// The only time it's safe to update the saved_state pointer is
|
||||
// while handling a SaveState event, so this API is only exposed for those
|
||||
// events
|
||||
/// An interface for saving application state during [MainEvent::SaveState] events
|
||||
///
|
||||
/// This interface is only available temporarily while handling a [MainEvent::SaveState] event.
|
||||
#[derive(Debug)]
|
||||
pub struct StateSaver<'a> {
|
||||
app: &'a AndroidAppInner,
|
||||
}
|
||||
|
||||
impl<'a> StateSaver<'a> {
|
||||
/// Stores the given `state` such that it will be available to load the next
|
||||
/// time that the application resumes.
|
||||
pub fn store(&self, state: &'a [u8]) {
|
||||
self.app.native_activity.set_saved_state(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface for loading application state during [MainEvent::Resume] events
|
||||
///
|
||||
/// This interface is only available temporarily while handling a [MainEvent::Resume] event.
|
||||
#[derive(Debug)]
|
||||
pub struct StateLoader<'a> {
|
||||
app: &'a AndroidAppInner,
|
||||
}
|
||||
impl<'a> StateLoader<'a> {
|
||||
/// Returns whatever state was saved during the last [MainEvent::SaveState] event or `None`
|
||||
pub fn load(&self) -> Option<Vec<u8>> {
|
||||
self.app.native_activity.saved_state()
|
||||
}
|
||||
}
|
||||
|
||||
/// A means to wake up the main thread while it is blocked waiting for I/O
|
||||
#[derive(Clone)]
|
||||
pub struct AndroidAppWaker {
|
||||
// The looper pointer is owned by the android_app and effectively
|
||||
@@ -77,6 +84,13 @@ unsafe impl Send for AndroidAppWaker {}
|
||||
unsafe impl Sync for AndroidAppWaker {}
|
||||
|
||||
impl AndroidAppWaker {
|
||||
|
||||
/// Interrupts the main thread if it is blocked within [`AndroidApp::poll_events()`]
|
||||
///
|
||||
/// If [`AndroidApp::poll_events()`] is interrupted it will invoke the poll
|
||||
/// callback with a [PollEvent::Wake][wake_event] event.
|
||||
///
|
||||
/// [wake_event]: crate::PollEvent::Wake
|
||||
pub fn wake(&self) {
|
||||
unsafe {
|
||||
ALooper_wake(self.looper.as_ptr());
|
||||
|
||||
Reference in New Issue
Block a user