@@ -6,6 +6,7 @@ use std::io::{BufRead, BufReader};
6
6
use std:: marker:: PhantomData ;
7
7
use std:: ops:: Deref ;
8
8
use std:: os:: unix:: prelude:: * ;
9
+ use std:: panic:: catch_unwind;
9
10
use std:: ptr:: NonNull ;
10
11
use std:: sync:: { Arc , RwLock } ;
11
12
use std:: time:: Duration ;
@@ -23,7 +24,7 @@ use ndk::asset::AssetManager;
23
24
use ndk:: configuration:: Configuration ;
24
25
use ndk:: native_window:: NativeWindow ;
25
26
26
- use crate :: util:: { abort_on_panic, android_log} ;
27
+ use crate :: util:: { abort_on_panic, android_log, log_panic } ;
27
28
use crate :: {
28
29
util, AndroidApp , ConfigurationRef , InputStatus , MainEvent , PollEvent , Rect , WindowManagerFlags ,
29
30
} ;
@@ -603,7 +604,8 @@ extern "Rust" {
603
604
// `app_main` function. This is run on a dedicated thread spawned
604
605
// by android_native_app_glue.
605
606
#[ 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 ) {
607
609
abort_on_panic ( || {
608
610
// Maybe make this stdout/stderr redirection an optional / opt-in feature?...
609
611
let mut logpipe: [ RawFd ; 2 ] = Default :: default ( ) ;
@@ -627,34 +629,51 @@ pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) {
627
629
}
628
630
} ) ;
629
631
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
+ }
633
644
634
- let app = AndroidApp :: from_ptr ( NonNull :: new ( app) . unwrap ( ) ) ;
645
+ jvm
646
+ } ;
635
647
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 ) ;
643
671
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
+ }
657
675
658
- ndk_context:: release_android_context ( ) ;
676
+ ndk_context:: release_android_context ( ) ;
677
+ }
659
678
} )
660
679
}
0 commit comments