21
21
22
22
use jni:: errors:: Error as JNIError ;
23
23
use jni:: objects:: { GlobalRef , JClass , JObject , JValue } ;
24
- use jni:: { JNIEnv , JavaVM } ;
24
+ use jni:: { AttachGuard , JNIEnv , JavaVM } ;
25
25
use once_cell:: sync:: OnceCell ;
26
26
27
27
static GLOBAL : OnceCell < Global > = OnceCell :: new ( ) ;
@@ -52,15 +52,15 @@ enum Global {
52
52
}
53
53
54
54
impl Global {
55
- fn env ( & self ) -> Result < JNIEnv < ' _ > , Error > {
55
+ fn env ( & self ) -> Result < AttachGuard < ' _ > , Error > {
56
56
let vm = match self {
57
57
Global :: Internal { java_vm, .. } => java_vm,
58
58
Global :: External ( global) => global. java_vm ( ) ,
59
59
} ;
60
- Ok ( vm. attach_current_thread_permanently ( ) ?)
60
+ Ok ( vm. attach_current_thread ( ) ?)
61
61
}
62
62
63
- fn context ( & self ) -> Result < Context < ' _ > , Error > {
63
+ fn context ( & self ) -> Result < ( GlobalContext , AttachGuard < ' _ > ) , Error > {
64
64
let env = self . env ( ) ?;
65
65
66
66
let context = match self {
@@ -73,14 +73,21 @@ impl Global {
73
73
Global :: External ( global) => global. class_loader ( ) ,
74
74
} ;
75
75
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
+ } ,
79
81
env,
80
- } )
82
+ ) )
81
83
}
82
84
}
83
85
86
+ struct GlobalContext {
87
+ context : GlobalRef ,
88
+ loader : GlobalRef ,
89
+ }
90
+
84
91
fn global ( ) -> & ' static Global {
85
92
GLOBAL
86
93
. get ( )
@@ -178,29 +185,18 @@ impl From<JNIError> for Error {
178
185
}
179
186
}
180
187
181
- pub ( super ) struct Context < ' a > {
182
- env : JNIEnv < ' a > ,
188
+ pub ( super ) struct LocalContext < ' a , ' env > {
189
+ env : & ' a mut JNIEnv < ' env > ,
183
190
context : GlobalRef ,
184
191
loader : GlobalRef ,
185
192
}
186
193
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 > {
199
195
/// Load a class from the application class loader
200
196
///
201
197
/// This should be used instead of `JNIEnv::find_class` to ensure all classes
202
198
/// 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 > {
204
200
let name = self . env . new_string ( name) ?;
205
201
let class = self . env . call_method (
206
202
& self . loader ,
@@ -211,21 +207,39 @@ impl<'a> Context<'a> {
211
207
212
208
Ok ( JObject :: try_from ( class) ?. into ( ) )
213
209
}
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
+ }
214
216
}
215
217
216
218
/// Borrow the Android application context and execute the closure
217
219
/// `with_context, ensuring locals are properly freed and exceptions
218
220
/// are cleared.
219
221
pub ( super ) fn with_context < F , T > ( f : F ) -> Result < T , Error >
220
222
where
221
- F : FnOnce ( & mut Context , & mut JNIEnv ) -> Result < T , Error > ,
223
+ F : FnOnce ( & mut LocalContext , & mut JNIEnv ) -> Result < T , Error > ,
222
224
{
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 ( ) } ;
226
232
227
233
// 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
+ } )
229
243
}
230
244
231
245
/// Loads and caches a class on first use
@@ -244,11 +258,11 @@ impl CachedClass {
244
258
}
245
259
246
260
/// 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 > {
248
262
let class = self . class . get_or_try_init ( || -> Result < _ , Error > {
249
263
let class = cx. load_class ( self . name ) ?;
250
264
251
- Ok ( cx. env ( ) . new_global_ref ( class) ?)
265
+ Ok ( cx. env . new_global_ref ( class) ?)
252
266
} ) ?;
253
267
254
268
Ok ( class. as_obj ( ) . into ( ) )
0 commit comments