Skip to content

Expose main thread's looper for runOnUiThread compatibility #198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions android-activity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,23 @@ impl AndroidApp {
self.inner.read().unwrap().native_window()
}

/// Returns a pointer to the current looper associated with the main thread
///
/// This is appropriate for posting callbacks that need to run on the UI thread
/// Wrap in an [`ndk::looper::ForeignLooper `]
/// ```ignore
/// # use ndk;
/// # let app: AndroidApp = todo!();
/// let looper = unsafe {
/// let non_null_looper = ptr::NonNull::new(_app.main_looper_as_ptr()).unwrap();
/// ndk::looper::ForeignLooper::from_ptr(non_null_looper)
/// };
/// looper.add_fd_with_callback(todo!(), ndk::looper::FdEvent::INPUT, todo!()).unwrap();
/// ```
pub fn main_looper_as_ptr(&self) -> *mut ndk_sys::ALooper {
self.inner.read().unwrap().main_looper()
}

/// Returns a pointer to the Java Virtual Machine, for making JNI calls
///
/// This returns a pointer to the Java Virtual Machine which can be used
Expand Down
3 changes: 2 additions & 1 deletion android-activity/src/native_activity/glue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,7 @@ extern "C" fn ANativeActivity_onCreate(
let rust_glue = jvm_glue.clone();
// Let us Send the NativeActivity pointer to the Rust main() thread without a wrapper type
let activity_ptr: libc::intptr_t = activity as _;
let main_looper_ptr: libc::intptr_t = unsafe { ndk_sys::ALooper_forThread() } as _;

// Note: we drop the thread handle which will detach the thread
std::thread::spawn(move || {
Expand All @@ -867,7 +868,7 @@ extern "C" fn ANativeActivity_onCreate(
jvm
});

let app = AndroidApp::new(rust_glue.clone(), jvm.clone());
let app = AndroidApp::new(rust_glue.clone(), jvm.clone(), main_looper_ptr as *mut _);

rust_glue.notify_main_thread_running();

Expand Down
16 changes: 15 additions & 1 deletion android-activity/src/native_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ impl AndroidAppWaker {
}

impl AndroidApp {
pub(crate) fn new(native_activity: NativeActivityGlue, jvm: CloneJavaVM) -> Self {
pub(crate) fn new(
native_activity: NativeActivityGlue,
jvm: CloneJavaVM,
main_looper_ptr: *mut ndk_sys::ALooper
) -> Self {
let mut env = jvm.get_env().unwrap(); // We attach to the thread before creating the AndroidApp

let key_map_binding = match KeyCharacterMapBinding::new(&mut env) {
Expand All @@ -103,6 +107,9 @@ impl AndroidApp {
looper: Looper {
ptr: ptr::null_mut(),
},
main_looper: Looper {
ptr: main_looper_ptr,
},
key_map_binding: Arc::new(key_map_binding),
key_maps: Mutex::new(HashMap::new()),
input_receiver: Mutex::new(None),
Expand Down Expand Up @@ -147,6 +154,9 @@ pub(crate) struct AndroidAppInner {
pub(crate) native_activity: NativeActivityGlue,
looper: Looper,

/// Looper associated with the activy's main thread, sometimes called the UI thread.
main_looper: Looper,

/// Shared JNI bindings for the `KeyCharacterMap` class
key_map_binding: Arc<KeyCharacterMapBinding>,

Expand Down Expand Up @@ -179,6 +189,10 @@ impl AndroidAppInner {
self.looper.ptr
}

pub fn main_looper(&self) -> *mut ndk_sys::ALooper {
self.main_looper.ptr
}

pub fn native_window(&self) -> Option<NativeWindow> {
self.native_activity.mutex.lock().unwrap().window.clone()
}
Expand Down