bind/java: use the class loader cached during JNI_OnLoad call.
This allows the application class loader to be used when the bind/java package or other part of JNI dynamically loads java classes from a non-Java thread. http://developer.android.com/training/articles/perf-jni.html#faq_FindClass Fixes golang/go#10668. Change-Id: I44df3a9362617fa6dd26ddf88247e4fdaee7c7e8 Reviewed-on: https://go-review.googlesource.com/9732 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
696139153c
commit
601608a0e0
|
@ -34,5 +34,4 @@ public final class Go {
|
|||
private static boolean running = false;
|
||||
|
||||
private static native void run(Context ctx);
|
||||
private static native void waitForRun();
|
||||
}
|
||||
|
|
|
@ -19,19 +19,6 @@
|
|||
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__)
|
||||
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__)
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
current_vm = vm;
|
||||
current_ctx = NULL;
|
||||
current_native_activity = NULL;
|
||||
|
||||
JNIEnv* env;
|
||||
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
static jclass find_class(JNIEnv *env, const char *class_name) {
|
||||
jclass clazz = (*env)->FindClass(env, class_name);
|
||||
if (clazz == NULL) {
|
||||
|
@ -50,6 +37,44 @@ static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const
|
|||
return m;
|
||||
}
|
||||
|
||||
static jclass app_class_loader;
|
||||
static jmethodID app_find_class_fn;
|
||||
|
||||
// app_find_class finds the given class using the class loader cached
|
||||
// during JNI_OnLoad. This allows the bind/java package and other
|
||||
// part of JNI to dynamically load java classes from non-Java threads.
|
||||
//
|
||||
// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
|
||||
jclass app_find_class(JNIEnv* env, const char* name) {
|
||||
jstring classname = (*env)->NewStringUTF(env, name);
|
||||
return (*env)->CallObjectMethod(env, app_class_loader, app_find_class_fn, classname);
|
||||
}
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env;
|
||||
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
current_vm = vm;
|
||||
current_ctx = NULL;
|
||||
current_native_activity = NULL;
|
||||
|
||||
// cache the class loader for use in JNI threads.
|
||||
jclass a_clazz = (*env)->FindClass(env, "go/Go");
|
||||
jclass clazz_clazz = (*env)->GetObjectClass(env, a_clazz);
|
||||
jclass class_loader_clazz = (*env)->FindClass(env, "java/lang/ClassLoader");
|
||||
jmethodID get_class_loader = (*env)->GetMethodID(env, clazz_clazz, "getClassLoader",
|
||||
"()Ljava/lang/ClassLoader;");
|
||||
jclass loader = (*env)->CallObjectMethod(env, a_clazz, get_class_loader);
|
||||
app_class_loader = (*env)->NewGlobalRef(env, loader);
|
||||
|
||||
app_find_class_fn = (*env)->GetMethodID(env, class_loader_clazz, "findClass",
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
static void init_from_context() {
|
||||
if (current_ctx == NULL) {
|
||||
return;
|
||||
|
@ -163,6 +188,7 @@ Java_go_Go_run(JNIEnv* env, jclass clazz, jobject ctx) {
|
|||
jclass context_clazz = find_class(env, "android/content/Context");
|
||||
jmethodID getassets = find_method(
|
||||
env, context_clazz, "getAssets", "()Landroid/content/res/AssetManager;");
|
||||
|
||||
// Prevent the java AssetManager from being GC'd
|
||||
jobject asset_manager_ref = (*env)->NewGlobalRef(
|
||||
env, (*env)->CallObjectMethod(env, current_ctx, getassets));
|
||||
|
|
|
@ -46,6 +46,8 @@ JavaVM* current_vm;
|
|||
// current_ctx is Android's android.context.Context. May be NULL.
|
||||
jobject current_ctx;
|
||||
|
||||
jclass app_find_class(JNIEnv* env, const char* name);
|
||||
|
||||
// current_native_activity is the Android ANativeActivity. May be NULL.
|
||||
ANativeActivity* current_native_activity;
|
||||
|
||||
|
@ -192,6 +194,12 @@ func (androidState) JavaVM() unsafe.Pointer {
|
|||
return unsafe.Pointer(C.current_vm)
|
||||
}
|
||||
|
||||
// ClassFinder returns a C function pointer for finding a given class using
|
||||
// the app class loader. (jclass) (*fn)(JNIEnv*, const char*).
|
||||
func (androidState) ClassFinder() unsafe.Pointer {
|
||||
return unsafe.Pointer(C.app_find_class)
|
||||
}
|
||||
|
||||
func (androidState) AndroidContext() unsafe.Pointer {
|
||||
return unsafe.Pointer(C.current_ctx)
|
||||
}
|
||||
|
|
|
@ -159,15 +159,28 @@ static void unpin_arrays(JNIEnv *env, mem *m) {
|
|||
m->pinned = NULL;
|
||||
}
|
||||
|
||||
static void describe_exception(JNIEnv* env) {
|
||||
jthrowable exc = (*env)->ExceptionOccurred(env);
|
||||
if (exc) {
|
||||
(*env)->ExceptionDescribe(env);
|
||||
(*env)->ExceptionClear(env);
|
||||
}
|
||||
}
|
||||
|
||||
// find_class_fn finds a class with the given name using the app class loader.
|
||||
// It is implemented in the app package and is initialized during the init_seq call.
|
||||
static jclass (*find_class_fn)(JNIEnv*, const char*);
|
||||
|
||||
static jfieldID find_field(JNIEnv *env, const char *class_name, const char *field_name, const char *field_type) {
|
||||
jclass clazz = (*env)->FindClass(env, class_name);
|
||||
jclass clazz = find_class_fn(env, class_name);
|
||||
if (clazz == NULL) {
|
||||
describe_exception(env);
|
||||
LOG_FATAL("cannot find %s", class_name);
|
||||
return NULL;
|
||||
}
|
||||
jfieldID id = (*env)->GetFieldID(env, clazz, field_name , field_type);
|
||||
if(id == NULL) {
|
||||
describe_exception(env);
|
||||
LOG_FATAL("no %s/%s field", field_name, field_type);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -175,26 +188,28 @@ static jfieldID find_field(JNIEnv *env, const char *class_name, const char *fiel
|
|||
}
|
||||
|
||||
static jclass find_class(JNIEnv *env, const char *class_name) {
|
||||
jclass clazz = (*env)->FindClass(env, class_name);
|
||||
jclass clazz = find_class_fn(env, class_name);
|
||||
if (clazz == NULL) {
|
||||
describe_exception(env);
|
||||
LOG_FATAL("cannot find %s", class_name);
|
||||
return NULL;
|
||||
}
|
||||
return (*env)->NewGlobalRef(env, clazz);
|
||||
}
|
||||
|
||||
void init_seq(void *javavm) {
|
||||
void init_seq(void *javavm, void *classfinder) {
|
||||
JavaVM *vm = (JavaVM*)javavm;
|
||||
find_class_fn = (jclass (*)(JNIEnv*, const char*))classfinder;
|
||||
|
||||
JNIEnv *env;
|
||||
int res = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6);
|
||||
if (res == JNI_EDETACHED) {
|
||||
if ((*vm)->AttachCurrentThread(vm, &env, NULL) != 0) {
|
||||
if ((*vm)->AttachCurrentThread(vm, &env, NULL) != JNI_OK) {
|
||||
LOG_FATAL("cannot attach to current_vm");
|
||||
}
|
||||
} else if (res != 0) {
|
||||
} else if (res != JNI_OK) {
|
||||
LOG_FATAL("bad vm env: %d", res);
|
||||
}
|
||||
|
||||
memptr_id = find_field(env, "go/Seq", "memptr", "J");
|
||||
receive_refnum_id = find_field(env, "go/Seq$Receive", "refnum", "I");
|
||||
receive_handle_id = find_field(env, "go/Seq$Receive", "handle", "I");
|
||||
|
|
|
@ -82,7 +82,11 @@ func initSeq() {
|
|||
vm := app.State.(interface {
|
||||
JavaVM() unsafe.Pointer
|
||||
}).JavaVM()
|
||||
C.init_seq(vm)
|
||||
classFinder := app.State.(interface {
|
||||
ClassFinder() unsafe.Pointer
|
||||
}).ClassFinder()
|
||||
|
||||
C.init_seq(vm, classFinder)
|
||||
}
|
||||
|
||||
func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) {
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
void init_seq(void* vm);
|
||||
void init_seq(void* vm, void* classfinder);
|
||||
|
|
Loading…
Reference in New Issue