I'm using Android NDK and need access to assets. A requirement for asset access seems to be obtaining an AssetManager reference.
Looking at the NDK samples (https://github.com/android/ndk-samples), the pattern seems to be:
- A
JNIEnv*
is passed into the func when called directly from the JavaVM, along with somejobject
- Use these to get
AAssetManager*
and then use this to open assets
That seems simple enough, except in my case, the functions are being called from Unity so I don't have access to either a JNIEnv*
or jobject
. Getting the JNIEnv*
seems easy enough as I can make use of JNI_OnLoad
to get access to a JavaVM*
and then use that to get a JNIEnv*
via vm->GetEnv
. My questions about this are:
1) My understanding is that, an Android app can only have one instance of a Java VM. Am I safe to take the JavaVM*
passed into JNI_OnLoad
and save it for use in other function calls?
2) What about the JNIEnv*
? Can I grab that once during JNI_OnLoad
and save it, or should I grab a fresh one every time I need to use assets within a function? Is JNIEnv*
something I need to explicitly free? (i.e. what's the lifetime/ownership situation with JNIEnv*
?)
3) AAssetManager_fromJava also requires a jobject
with the documentation (https://developer.android.com/ndk/reference/group/asset#group___asset_1gadfd6537af41577735bcaee52120127f4) saying: "Note that the caller is responsible for obtaining and holding a VM reference to the jobject to prevent its being garbage collected while the native object is in use.". I've seem some examples that simply pass in an empty (native) string like AAssetManager_fromJava(env, "");
- is that ok? I'd only be using the AssetManager for the lifetime of that call, and I could get a fresh one each time. (Again, is AAssetManager*
a resource I need to manage, or am I just getting a reference to something owned elsewhere? The documentation seems to imply the latter.)
4) So given all the above, I'd probably do something like:
JavaVM* g_vm;
JNIEnv* g_env;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
g_vm = vm;
g_vm->GetEnv((void **)&g_env, JNI_VERSION_1_6); // TODO: error checking
return JNI_VERSION_1_6;
}
void do_asset_stuff() {
AAssetManager* mgr = AAssetManager_fromJava(g_env, "");
// do stuff...
}
Is that reasonable? No memory/resource leak issues? Any issues with multi-threading?
Thanks!
EDIT: Seems like there are some threading considerations with JNIEnv*
. See: Unable to get JNIEnv* value in arbitrary context