@@ -6,6 +6,7 @@ use std::io::{BufRead, BufReader};
66use std:: marker:: PhantomData ;
77use std:: ops:: Deref ;
88use std:: os:: unix:: prelude:: * ;
9+ use std:: panic:: catch_unwind;
910use std:: ptr:: NonNull ;
1011use std:: sync:: { Arc , RwLock } ;
1112use std:: time:: Duration ;
@@ -23,7 +24,7 @@ use ndk::asset::AssetManager;
2324use ndk:: configuration:: Configuration ;
2425use ndk:: native_window:: NativeWindow ;
2526
26- use crate :: util:: { abort_on_panic, android_log} ;
27+ use crate :: util:: { abort_on_panic, android_log, log_panic } ;
2728use crate :: {
2829 util, AndroidApp , ConfigurationRef , InputStatus , MainEvent , PollEvent , Rect , WindowManagerFlags ,
2930} ;
@@ -603,7 +604,8 @@ extern "Rust" {
603604// `app_main` function. This is run on a dedicated thread spawned
604605// by android_native_app_glue.
605606#[ no_mangle]
606- pub unsafe extern "C" fn _rust_glue_entry ( app : * mut ffi:: android_app ) {
607+ #[ allow( unused_unsafe) ] // Otherwise rust 1.64 moans about using unsafe{} in unsafe functions
608+ pub unsafe extern "C" fn _rust_glue_entry ( native_app : * mut ffi:: android_app ) {
607609 abort_on_panic ( || {
608610 // Maybe make this stdout/stderr redirection an optional / opt-in feature?...
609611 let mut logpipe: [ RawFd ; 2 ] = Default :: default ( ) ;
@@ -627,34 +629,51 @@ pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) {
627629 }
628630 } ) ;
629631
630- let jvm: * mut JavaVM = ( * ( * app) . activity ) . vm ;
631- let activity: jobject = ( * ( * app) . activity ) . javaGameActivity ;
632- ndk_context:: initialize_android_context ( jvm. cast ( ) , activity. cast ( ) ) ;
632+ let jvm = unsafe {
633+ let jvm = ( * ( * native_app) . activity ) . vm ;
634+ let activity: jobject = ( * ( * native_app) . activity ) . javaGameActivity ;
635+ ndk_context:: initialize_android_context ( jvm. cast ( ) , activity. cast ( ) ) ;
636+
637+ // Since this is a newly spawned thread then the JVM hasn't been attached
638+ // to the thread yet. Attach before calling the applications main function
639+ // so they can safely make JNI calls
640+ let mut jenv_out: * mut core:: ffi:: c_void = std:: ptr:: null_mut ( ) ;
641+ if let Some ( attach_current_thread) = ( * ( * jvm) ) . AttachCurrentThread {
642+ attach_current_thread ( jvm, & mut jenv_out, std:: ptr:: null_mut ( ) ) ;
643+ }
633644
634- let app = AndroidApp :: from_ptr ( NonNull :: new ( app) . unwrap ( ) ) ;
645+ jvm
646+ } ;
635647
636- // Since this is a newly spawned thread then the JVM hasn't been attached
637- // to the thread yet. Attach before calling the applications main function
638- // so they can safely make JNI calls
639- let mut jenv_out: * mut core:: ffi:: c_void = std:: ptr:: null_mut ( ) ;
640- if let Some ( attach_current_thread) = ( * ( * jvm) ) . AttachCurrentThread {
641- attach_current_thread ( jvm, & mut jenv_out, std:: ptr:: null_mut ( ) ) ;
642- }
648+ unsafe {
649+ let app = AndroidApp :: from_ptr ( NonNull :: new ( native_app) . unwrap ( ) ) ;
650+
651+ // We want to specifically catch any panic from the application's android_main
652+ // so we can finish + destroy the Activity gracefully via the JVM
653+ catch_unwind ( || {
654+ // XXX: If we were in control of the Java Activity subclass then
655+ // we could potentially run the android_main function via a Java native method
656+ // springboard (e.g. call an Activity subclass method that calls a jni native
657+ // method that then just calls android_main()) that would make sure there was
658+ // a Java frame at the base of our call stack which would then be recognised
659+ // when calling FindClass to lookup a suitable classLoader, instead of
660+ // defaulting to the system loader. Without this then it's difficult for native
661+ // code to look up non-standard Java classes.
662+ android_main ( app) ;
663+ } )
664+ . unwrap_or_else ( |panic| log_panic ( panic) ) ;
665+
666+ // Let JVM know that our Activity can be destroyed before detaching from the JVM
667+ //
668+ // "Note that this method can be called from any thread; it will send a message
669+ // to the main thread of the process where the Java finish call will take place"
670+ ffi:: GameActivity_finish ( ( * native_app) . activity ) ;
643671
644- // XXX: If we were in control of the Java Activity subclass then
645- // we could potentially run the android_main function via a Java native method
646- // springboard (e.g. call an Activity subclass method that calls a jni native
647- // method that then just calls android_main()) that would make sure there was
648- // a Java frame at the base of our call stack which would then be recognised
649- // when calling FindClass to lookup a suitable classLoader, instead of
650- // defaulting to the system loader. Without this then it's difficult for native
651- // code to look up non-standard Java classes.
652- android_main ( app) ;
653-
654- if let Some ( detach_current_thread) = ( * ( * jvm) ) . DetachCurrentThread {
655- detach_current_thread ( jvm) ;
656- }
672+ if let Some ( detach_current_thread) = ( * ( * jvm) ) . DetachCurrentThread {
673+ detach_current_thread ( jvm) ;
674+ }
657675
658- ndk_context:: release_android_context ( ) ;
676+ ndk_context:: release_android_context ( ) ;
677+ }
659678 } )
660679}
0 commit comments