2121
2222use jni:: errors:: Error as JNIError ;
2323use jni:: objects:: { GlobalRef , JClass , JObject , JValue } ;
24- use jni:: { JNIEnv , JavaVM } ;
24+ use jni:: { AttachGuard , JNIEnv , JavaVM } ;
2525use once_cell:: sync:: OnceCell ;
2626
2727static GLOBAL : OnceCell < Global > = OnceCell :: new ( ) ;
@@ -52,15 +52,15 @@ enum Global {
5252}
5353
5454impl Global {
55- fn env ( & self ) -> Result < JNIEnv < ' _ > , Error > {
55+ fn env ( & self ) -> Result < AttachGuard < ' _ > , Error > {
5656 let vm = match self {
5757 Global :: Internal { java_vm, .. } => java_vm,
5858 Global :: External ( global) => global. java_vm ( ) ,
5959 } ;
60- Ok ( vm. attach_current_thread_permanently ( ) ?)
60+ Ok ( vm. attach_current_thread ( ) ?)
6161 }
6262
63- fn context ( & self ) -> Result < Context < ' _ > , Error > {
63+ fn context ( & self ) -> Result < ( GlobalContext , AttachGuard < ' _ > ) , Error > {
6464 let env = self . env ( ) ?;
6565
6666 let context = match self {
@@ -73,14 +73,21 @@ impl Global {
7373 Global :: External ( global) => global. class_loader ( ) ,
7474 } ;
7575
76- Ok ( Context {
77- context : env. new_global_ref ( context) ?,
78- loader : env. new_global_ref ( loader) ?,
76+ Ok ( (
77+ GlobalContext {
78+ context : env. new_global_ref ( context) ?,
79+ loader : env. new_global_ref ( loader) ?,
80+ } ,
7981 env,
80- } )
82+ ) )
8183 }
8284}
8385
86+ struct GlobalContext {
87+ context : GlobalRef ,
88+ loader : GlobalRef ,
89+ }
90+
8491fn global ( ) -> & ' static Global {
8592 GLOBAL
8693 . get ( )
@@ -178,29 +185,18 @@ impl From<JNIError> for Error {
178185 }
179186}
180187
181- pub ( super ) struct Context < ' a > {
182- env : JNIEnv < ' a > ,
188+ pub ( super ) struct LocalContext < ' a , ' env > {
189+ env : & ' a mut JNIEnv < ' env > ,
183190 context : GlobalRef ,
184191 loader : GlobalRef ,
185192}
186193
187- impl < ' a > Context < ' a > {
188- /// Borrow a reference to the JNI Environment executing the Android application
189- pub ( super ) fn env ( & mut self ) -> & mut JNIEnv < ' a > {
190- & mut self . env
191- }
192-
193- /// Borrow the `applicationContext` from the Android application
194- /// <https://developer.android.com/reference/android/app/Application>
195- pub ( super ) fn application_context ( & self ) -> & JObject < ' a > {
196- & self . context
197- }
198-
194+ impl < ' a , ' env > LocalContext < ' a , ' env > {
199195 /// Load a class from the application class loader
200196 ///
201197 /// This should be used instead of `JNIEnv::find_class` to ensure all classes
202198 /// in the application can be found.
203- pub ( super ) fn load_class ( & mut self , name : & str ) -> Result < JClass < ' a > , Error > {
199+ fn load_class ( & mut self , name : & str ) -> Result < JClass < ' env > , Error > {
204200 let name = self . env . new_string ( name) ?;
205201 let class = self . env . call_method (
206202 & self . loader ,
@@ -211,21 +207,39 @@ impl<'a> Context<'a> {
211207
212208 Ok ( JObject :: try_from ( class) ?. into ( ) )
213209 }
210+
211+ /// Borrow the `applicationContext` from the Android application
212+ /// <https://developer.android.com/reference/android/app/Application>
213+ pub ( super ) fn application_context ( & self ) -> & JObject < ' _ > {
214+ & self . context
215+ }
214216}
215217
216218/// Borrow the Android application context and execute the closure
217219/// `with_context, ensuring locals are properly freed and exceptions
218220/// are cleared.
219221pub ( super ) fn with_context < F , T > ( f : F ) -> Result < T , Error >
220222where
221- F : FnOnce ( & mut Context , & mut JNIEnv ) -> Result < T , Error > ,
223+ F : FnOnce ( & mut LocalContext , & mut JNIEnv ) -> Result < T , Error > ,
222224{
223- let mut context = global ( ) . context ( ) ?;
224- let mut binding = global ( ) . context ( ) ?;
225- let env = binding. env ( ) ;
225+ let ( global_context, mut binding_env) = global ( ) . context ( ) ?;
226+ // SAFETY: Any local references created with this env are always destroyed prior to the parent
227+ // frame exiting because we force it to be dropped before the new frame exits and don't allow
228+ // the closure to access the env directly. We don't use it anywhere outside that sub-scope either.
229+ //
230+ // Rust's borrowing rules enforce that any reference that came from this env must be dropped before it is too.
231+ let ctx_env = unsafe { binding_env. unsafe_clone ( ) } ;
226232
227233 // 16 is the default capacity in the JVM, we can make this configurable if necessary
228- env. with_local_frame ( 16 , |env| f ( & mut context, env) )
234+ binding_env. with_local_frame ( 16 , |env| {
235+ let mut ctx_env = ctx_env;
236+ let mut context = LocalContext {
237+ env : & mut ctx_env,
238+ context : global_context. context ,
239+ loader : global_context. loader ,
240+ } ;
241+ f ( & mut context, env)
242+ } )
229243}
230244
231245/// Loads and caches a class on first use
@@ -244,11 +258,11 @@ impl CachedClass {
244258 }
245259
246260 /// Gets the cached class reference, loaded on first use
247- pub ( super ) fn get < ' a : ' b , ' b > ( & ' a self , cx : & mut Context < ' b > ) -> Result < & ' a JClass < ' b > , Error > {
261+ pub ( super ) fn get ( & self , cx : & mut LocalContext ) -> Result < & JClass < ' _ > , Error > {
248262 let class = self . class . get_or_try_init ( || -> Result < _ , Error > {
249263 let class = cx. load_class ( self . name ) ?;
250264
251- Ok ( cx. env ( ) . new_global_ref ( class) ?)
265+ Ok ( cx. env . new_global_ref ( class) ?)
252266 } ) ?;
253267
254268 Ok ( class. as_obj ( ) . into ( ) )
0 commit comments