How to get the Main Thread / UI Thread's Looper? #197
Replies: 4 comments 3 replies
-
Hi, yes I think there are multiple reasons why this thread - spawned by Here's an example of a hack that people have done in @rib how do you think we can best provide integration for this? |
Beta Was this translation helpful? Give feedback.
-
Great news, I thought I was going crazy and went down rabbit holes trying to get the JNI android.os.Looper.getMainLooper() into some kind of ALooper pointer. Is the AndroidInner's looper not the main? I tried to quickly get access to it via impl AndroidApp {
pub fn main_looper(&self) -> *mut ::ndk_sys::ALooper {
self.inner.read().unwrap().looper()
}
/// ...
let looper = unsafe {
let ptr = _app.main_looper();
if ptr.is_null() {
debug!("main looper is null!!");
return Err(());
}
let non_null_looper = ptr::NonNull::new(unsafe{ _app.main_looper() as *mut ndk_sys::ALooper }).unwrap();
ndk::looper::ForeignLooper::from_ptr(non_null_looper)
};
looper.add_fd_with_callback(pipe.reader.as_fd(), ::ndk::looper::FdEvent::INPUT, callback).expect("looper"); Same null pointer exception about not calling prepare() on the looper inside I'm assuming the inner looper is the main looper and it communicates with the rust thread via events, and that I can add a new fd with a callback and that callback will be executed in the main looper's thread. ? Maybe one of my assumptions is wrong. EDIT |
Beta Was this translation helpful? Give feedback.
-
OK, in glue.rs I can wrap the aLooper ptr in a crate local Looper for the unsafe Send + Sync // ANative'activit_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: Looper = unsafe{ Looper{ptr: ::ndk_sys::ALooper_forThread()}};
// Note: we drop the thread handle which will detach the thread
std::thread::spawn(move || {
let activity: *mut ndk_sys::ANativeActivity = activity_ptr as *mut _;
let jvm = abort_on_panic(|| unsafe {
let na = activity;
let jvm: *mut jni_sys::JavaVM = (*na).vm;
let activity = (*na).clazz; // Completely bogus name; this is the _instance_ not class pointer
ndk_context::initialize_android_context(jvm.cast(), activity.cast());
let jvm = CloneJavaVM::from_raw(jvm).unwrap();
// 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
jvm.attach_current_thread_permanently().unwrap();
jvm
});
let app = AndroidApp::new(rust_glue.clone(), jvm.clone(), main_looper); then just add a main_looper to the AndroidInner and expose via AndroidApp::main_looper() impl AndroidApp {
pub(crate) fn new(native_activity: NativeActivityGlue, jvm: CloneJavaVM, main_looper: Looper) -> 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) {
Ok(b) => b,
Err(err) => {
panic!("Failed to create KeyCharacterMap JNI bindings: {err:?}");
}
};
let app = Self {
inner: Arc::new(RwLock::new(AndroidAppInner {
jvm,
native_activity,
looper: Looper {
ptr: ptr::null_mut(),
},
main_looper,
key_map_binding: Arc::new(key_map_binding),
key_maps: Mutex::new(HashMap::new()),
input_receiver: Mutex::new(None),
})),
}; I'm not doing any clean-up or anything. |
Beta Was this translation helpful? Give feedback.
-
The way #[derive(Clone, Copy)]
pub struct JniHandle;
impl JniHandle {
/// Execute jni code on the thread of the webview.
/// Provided function will be provided with the jni evironment, Android activity and WebView
pub fn exec<F>(&self, func: F)
where
F: FnOnce(&mut JNIEnv, &JObject, &JObject) + Send + 'static,
{
MainPipe::send(WebViewMessage::Jni(Box::new(func)));
}
} |
Beta Was this translation helpful? Give feedback.
-
Is it possible to post messages to the UI thread from android_main()?
It seems like android-activity creates its own thread for Rust, this is not the UI thread, so trying to post a simple Toast results in
Even throwing in a Threadlooper::prepare() seems to grab the Rust local thread.
I can see that the AppInner has the looper and native_activity, but I'm unsure how to communicate with them to handle UI clicks.
Beta Was this translation helpful? Give feedback.
All reactions