diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 969ac568335..8339ff6ae3a 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -123,7 +123,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, if (ref.is_valid()) { if (Object::cast_to(ref.ptr())) { Ref jo = ref; - jclass c = env->FindClass(cn.utf8().get_data()); + jclass c = jni_find_class(env, cn.utf8().get_data()); if (!c || !env->IsInstanceOf(jo->instance, c)) { arg_expected = Variant::OBJECT; } else { @@ -223,7 +223,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, if (cn.begins_with("[L") && cn.ends_with(";")) { cn = cn.substr(2, cn.length() - 3); } - jclass c = env->FindClass(cn.utf8().get_data()); + jclass c = jni_find_class(env, cn.utf8().get_data()); if (c) { for (int j = 0; j < arr.size(); j++) { Ref jo = arr[j]; @@ -316,7 +316,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, argv[i].d = *p_args[i]; } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BOOLEAN: { - jclass bclass = env->FindClass("java/lang/Boolean"); + jclass bclass = jni_find_class(env, "java/lang/Boolean"); jmethodID ctor = env->GetMethodID(bclass, "", "(Z)V"); jvalue val; val.z = (bool)(*p_args[i]); @@ -325,7 +325,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BYTE: { - jclass bclass = env->FindClass("java/lang/Byte"); + jclass bclass = jni_find_class(env, "java/lang/Byte"); jmethodID ctor = env->GetMethodID(bclass, "", "(B)V"); jvalue val; val.b = (int)(*p_args[i]); @@ -334,7 +334,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_CHAR: { - jclass bclass = env->FindClass("java/lang/Character"); + jclass bclass = jni_find_class(env, "java/lang/Character"); jmethodID ctor = env->GetMethodID(bclass, "", "(C)V"); jvalue val; val.c = (int)(*p_args[i]); @@ -343,7 +343,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_SHORT: { - jclass bclass = env->FindClass("java/lang/Short"); + jclass bclass = jni_find_class(env, "java/lang/Short"); jmethodID ctor = env->GetMethodID(bclass, "", "(S)V"); jvalue val; val.s = (int)(*p_args[i]); @@ -352,7 +352,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_INT: { - jclass bclass = env->FindClass("java/lang/Integer"); + jclass bclass = jni_find_class(env, "java/lang/Integer"); jmethodID ctor = env->GetMethodID(bclass, "", "(I)V"); jvalue val; val.i = (int)(*p_args[i]); @@ -361,7 +361,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_LONG: { - jclass bclass = env->FindClass("java/lang/Long"); + jclass bclass = jni_find_class(env, "java/lang/Long"); jmethodID ctor = env->GetMethodID(bclass, "", "(J)V"); jvalue val; val.j = (int64_t)(*p_args[i]); @@ -370,7 +370,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_FLOAT: { - jclass bclass = env->FindClass("java/lang/Float"); + jclass bclass = jni_find_class(env, "java/lang/Float"); jmethodID ctor = env->GetMethodID(bclass, "", "(F)V"); jvalue val; val.f = (float)(*p_args[i]); @@ -379,7 +379,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE: { - jclass bclass = env->FindClass("java/lang/Double"); + jclass bclass = jni_find_class(env, "java/lang/Double"); jmethodID ctor = env->GetMethodID(bclass, "", "(D)V"); jvalue val; val.d = (double)(*p_args[i]); @@ -572,7 +572,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, if (p_args[i]->get_type() == Variant::ARRAY) { Array arr = *p_args[i]; - a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr); + a = env->NewObjectArray(arr.size(), jni_find_class(env, "java/lang/String"), nullptr); for (int j = 0; j < arr.size(); j++) { String s = arr[j]; jstring jStr = env->NewStringUTF(s.utf8().get_data()); @@ -581,7 +581,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } } else if (p_args[i]->get_type() == Variant::PACKED_STRING_ARRAY) { PackedStringArray arr = *p_args[i]; - a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr); + a = env->NewObjectArray(arr.size(), jni_find_class(env, "java/lang/String"), nullptr); for (int j = 0; j < arr.size(); j++) { String s = arr[j]; jstring jStr = env->NewStringUTF(s.utf8().get_data()); @@ -595,7 +595,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, } break; case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: { Array arr = *p_args[i]; - jobjectArray jarr = env->NewObjectArray(arr.size(), env->FindClass("org/godotengine/godot/variant/Callable"), nullptr); + jobjectArray jarr = env->NewObjectArray(arr.size(), jni_find_class(env, "org/godotengine/godot/variant/Callable"), nullptr); for (int j = 0; j < arr.size(); j++) { Variant callable = arr[j]; jobject jcallable = callable_to_jcallable(env, callable); @@ -611,7 +611,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, if (cn.begins_with("[L") && cn.ends_with(";")) { cn = cn.substr(2, cn.length() - 3); } - jclass c = env->FindClass(cn.utf8().get_data()); + jclass c = jni_find_class(env, cn.utf8().get_data()); if (c) { Array arr = *p_args[i]; jobjectArray jarr = env->NewObjectArray(arr.size(), c, nullptr); @@ -1470,7 +1470,7 @@ Ref JavaClassWrapper::_wrap(const String &p_class, bool p_allow_priva JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, Ref()); - jclass bclass = env->FindClass(class_name_dots.replace_char('.', '/').utf8().get_data()); + jclass bclass = jni_find_class(env, class_name_dots.replace_char('.', '/').utf8().get_data()); ERR_FAIL_NULL_V_MSG(bclass, Ref(), vformat("Java class '%s' not found.", p_class)); jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredConstructors); @@ -1699,7 +1699,7 @@ JavaClassWrapper::JavaClassWrapper() { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL(env); - jclass bclass = env->FindClass("java/lang/Class"); + jclass bclass = jni_find_class(env, "java/lang/Class"); Class_getDeclaredConstructors = env->GetMethodID(bclass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); Class_getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); Class_getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;"); @@ -1707,53 +1707,53 @@ JavaClassWrapper::JavaClassWrapper() { Class_getSuperclass = env->GetMethodID(bclass, "getSuperclass", "()Ljava/lang/Class;"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/reflect/Constructor"); + bclass = jni_find_class(env, "java/lang/reflect/Constructor"); Constructor_getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;"); Constructor_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/reflect/Method"); + bclass = jni_find_class(env, "java/lang/reflect/Method"); Method_getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;"); Method_getReturnType = env->GetMethodID(bclass, "getReturnType", "()Ljava/lang/Class;"); Method_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;"); Method_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/reflect/Field"); + bclass = jni_find_class(env, "java/lang/reflect/Field"); Field_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;"); Field_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I"); Field_get = env->GetMethodID(bclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Boolean"); + bclass = jni_find_class(env, "java/lang/Boolean"); Boolean_booleanValue = env->GetMethodID(bclass, "booleanValue", "()Z"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Byte"); + bclass = jni_find_class(env, "java/lang/Byte"); Byte_byteValue = env->GetMethodID(bclass, "byteValue", "()B"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Character"); + bclass = jni_find_class(env, "java/lang/Character"); Character_characterValue = env->GetMethodID(bclass, "charValue", "()C"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Short"); + bclass = jni_find_class(env, "java/lang/Short"); Short_shortValue = env->GetMethodID(bclass, "shortValue", "()S"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Integer"); + bclass = jni_find_class(env, "java/lang/Integer"); Integer_integerValue = env->GetMethodID(bclass, "intValue", "()I"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Long"); + bclass = jni_find_class(env, "java/lang/Long"); Long_longValue = env->GetMethodID(bclass, "longValue", "()J"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Float"); + bclass = jni_find_class(env, "java/lang/Float"); Float_floatValue = env->GetMethodID(bclass, "floatValue", "()F"); env->DeleteLocalRef(bclass); - bclass = env->FindClass("java/lang/Double"); + bclass = jni_find_class(env, "java/lang/Double"); Double_doubleValue = env->GetMethodID(bclass, "doubleValue", "()D"); env->DeleteLocalRef(bclass); } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index cfe3832fdda..c7a549f1a85 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -119,6 +119,8 @@ static void _terminate(JNIEnv *env, bool p_restart = false) { FileAccessFilesystemJAndroid::terminate(); NetSocketAndroid::terminate(); + cleanup_android_class_loader(); + if (godot_java) { godot_java->on_godot_terminating(env); if (!restart_on_cleanup) { @@ -144,12 +146,13 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv JavaVM *jvm; env->GetJavaVM(&jvm); + init_thread_jandroid(jvm, env); + setup_android_class_loader(); + // create our wrapper classes godot_java = new GodotJavaWrapper(env, p_godot_instance); godot_io_java = new GodotIOJavaWrapper(env, p_godot_io); - init_thread_jandroid(jvm, env); - FileAccessAndroid::setup(p_asset_manager); DirAccessJAndroid::setup(p_directory_access_handler); FileAccessFilesystemJAndroid::setup(p_file_access_handler); @@ -478,7 +481,7 @@ JNIEXPORT jobjectArray JNICALL Java_org_godotengine_godot_GodotLib_getRendererIn String rendering_driver = RenderingServer::get_singleton()->get_current_rendering_driver_name(); String rendering_method = RenderingServer::get_singleton()->get_current_rendering_method(); - jobjectArray result = env->NewObjectArray(2, env->FindClass("java/lang/String"), nullptr); + jobjectArray result = env->NewObjectArray(2, jni_find_class(env, "java/lang/String"), nullptr); env->SetObjectArrayElement(result, 0, env->NewStringUTF(rendering_driver.utf8().get_data())); env->SetObjectArrayElement(result, 1, env->NewStringUTF(rendering_method.utf8().get_data())); diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index dfd4849d8b5..14fa75f5f1a 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -41,7 +41,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { godot_instance = p_env->NewGlobalRef(p_godot_instance); // get info about our Godot class so we can get pointers and stuff... - godot_class = p_env->FindClass("org/godotengine/godot/Godot"); + godot_class = jni_find_class(p_env, "org/godotengine/godot/Godot"); if (godot_class) { godot_class = (jclass)p_env->NewGlobalRef(godot_class); } else { @@ -308,7 +308,7 @@ Error GodotJavaWrapper::show_dialog(const String &p_title, const String &p_descr ERR_FAIL_NULL_V(env, ERR_UNCONFIGURED); jstring j_title = env->NewStringUTF(p_title.utf8().get_data()); jstring j_description = env->NewStringUTF(p_description.utf8().get_data()); - jobjectArray j_buttons = env->NewObjectArray(p_buttons.size(), env->FindClass("java/lang/String"), nullptr); + jobjectArray j_buttons = env->NewObjectArray(p_buttons.size(), jni_find_class(env, "java/lang/String"), nullptr); for (int i = 0; i < p_buttons.size(); ++i) { jstring j_button = env->NewStringUTF(p_buttons[i].utf8().get_data()); env->SetObjectArrayElement(j_buttons, i, j_button); @@ -353,7 +353,7 @@ Error GodotJavaWrapper::show_file_picker(const String &p_current_directory, cons filters.append_array(E.get_slicec(';', 0).split(",")); // Add extensions. filters.append_array(E.get_slicec(';', 2).split(",")); // Add MIME types. } - jobjectArray j_filters = env->NewObjectArray(filters.size(), env->FindClass("java/lang/String"), nullptr); + jobjectArray j_filters = env->NewObjectArray(filters.size(), jni_find_class(env, "java/lang/String"), nullptr); for (int i = 0; i < filters.size(); ++i) { jstring j_filter = env->NewStringUTF(filters[i].utf8().get_data()); env->SetObjectArrayElement(j_filters, i, j_filter); @@ -469,7 +469,7 @@ int GodotJavaWrapper::create_new_godot_instance(const List &args) { if (_create_new_godot_instance) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, 0); - jobjectArray jargs = env->NewObjectArray(args.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); + jobjectArray jargs = env->NewObjectArray(args.size(), jni_find_class(env, "java/lang/String"), env->NewStringUTF("")); int i = 0; for (List::ConstIterator itr = args.begin(); itr != args.end(); ++itr, ++i) { jstring j_arg = env->NewStringUTF(itr->utf8().get_data()); diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp index baf3044b348..b6aca557806 100644 --- a/platform/android/jni_utils.cpp +++ b/platform/android/jni_utils.cpp @@ -32,6 +32,9 @@ #include "api/java_class_wrapper.h" +static jobject android_class_loader = nullptr; +static jmethodID load_class_method = nullptr; + jobject callable_to_jcallable(JNIEnv *p_env, const Variant &p_callable) { ERR_FAIL_NULL_V(p_env, nullptr); if (p_callable.get_type() != Variant::CALLABLE) { @@ -40,7 +43,7 @@ jobject callable_to_jcallable(JNIEnv *p_env, const Variant &p_callable) { Variant *callable_jcopy = memnew(Variant(p_callable)); - jclass bclass = p_env->FindClass("org/godotengine/godot/variant/Callable"); + jclass bclass = jni_find_class(p_env, "org/godotengine/godot/variant/Callable"); jmethodID ctor = p_env->GetMethodID(bclass, "", "(J)V"); jobject jcallable = p_env->NewObject(bclass, ctor, reinterpret_cast(callable_jcopy)); p_env->DeleteLocalRef(bclass); @@ -52,7 +55,7 @@ Callable jcallable_to_callable(JNIEnv *p_env, jobject p_jcallable_obj) { ERR_FAIL_NULL_V(p_env, Callable()); const Variant *callable_variant = nullptr; - jclass callable_class = p_env->FindClass("org/godotengine/godot/variant/Callable"); + jclass callable_class = jni_find_class(p_env, "org/godotengine/godot/variant/Callable"); if (callable_class && p_env->IsInstanceOf(p_jcallable_obj, callable_class)) { jmethodID get_native_pointer = p_env->GetMethodID(callable_class, "getNativePointer", "()J"); jlong native_callable = p_env->CallLongMethod(p_jcallable_obj, get_native_pointer); @@ -70,7 +73,7 @@ String charsequence_to_string(JNIEnv *p_env, jobject p_charsequence) { ERR_FAIL_NULL_V(p_env, String()); String result; - jclass bclass = p_env->FindClass("java/lang/CharSequence"); + jclass bclass = jni_find_class(p_env, "java/lang/CharSequence"); if (bclass && p_env->IsInstanceOf(p_charsequence, bclass)) { jmethodID to_string = p_env->GetMethodID(bclass, "toString", "()Ljava/lang/String;"); jstring obj_string = (jstring)p_env->CallObjectMethod(p_charsequence, to_string); @@ -89,7 +92,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a switch (p_type) { case Variant::BOOL: { if (force_jobject) { - jclass bclass = env->FindClass("java/lang/Boolean"); + jclass bclass = jni_find_class(env, "java/lang/Boolean"); jmethodID ctor = env->GetMethodID(bclass, "", "(Z)V"); jvalue val; val.z = (bool)(*p_arg); @@ -103,7 +106,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } break; case Variant::INT: { if (force_jobject) { - jclass bclass = env->FindClass("java/lang/Integer"); + jclass bclass = jni_find_class(env, "java/lang/Integer"); jmethodID ctor = env->GetMethodID(bclass, "", "(I)V"); jvalue val; val.i = (int)(*p_arg); @@ -118,7 +121,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } break; case Variant::FLOAT: { if (force_jobject) { - jclass bclass = env->FindClass("java/lang/Double"); + jclass bclass = jni_find_class(env, "java/lang/Double"); jmethodID ctor = env->GetMethodID(bclass, "", "(D)V"); jvalue val; val.d = (double)(*p_arg); @@ -139,7 +142,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } break; case Variant::PACKED_STRING_ARRAY: { Vector sarray = *p_arg; - jobjectArray arr = env->NewObjectArray(sarray.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); + jobjectArray arr = env->NewObjectArray(sarray.size(), jni_find_class(env, "java/lang/String"), env->NewStringUTF("")); for (int j = 0; j < sarray.size(); j++) { jstring str = env->NewStringUTF(sarray[j].utf8().get_data()); @@ -159,13 +162,13 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a case Variant::DICTIONARY: { Dictionary dict = *p_arg; - jclass dclass = env->FindClass("org/godotengine/godot/Dictionary"); + jclass dclass = jni_find_class(env, "org/godotengine/godot/Dictionary"); jmethodID ctor = env->GetMethodID(dclass, "", "()V"); jobject jdict = env->NewObject(dclass, ctor); Array keys = dict.keys(); - jobjectArray jkeys = env->NewObjectArray(keys.size(), env->FindClass("java/lang/String"), env->NewStringUTF("")); + jobjectArray jkeys = env->NewObjectArray(keys.size(), jni_find_class(env, "java/lang/String"), env->NewStringUTF("")); for (int j = 0; j < keys.size(); j++) { jstring str = env->NewStringUTF(String(keys[j]).utf8().get_data()); env->SetObjectArrayElement(jkeys, j, str); @@ -178,7 +181,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a env->CallVoidMethodA(jdict, set_keys, &val); env->DeleteLocalRef(jkeys); - jobjectArray jvalues = env->NewObjectArray(keys.size(), env->FindClass("java/lang/Object"), nullptr); + jobjectArray jvalues = env->NewObjectArray(keys.size(), jni_find_class(env, "java/lang/Object"), nullptr); for (int j = 0; j < keys.size(); j++) { Variant var = dict[keys[j]]; @@ -201,7 +204,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a case Variant::ARRAY: { Array array = *p_arg; - jobjectArray arr = env->NewObjectArray(array.size(), env->FindClass("java/lang/Object"), nullptr); + jobjectArray arr = env->NewObjectArray(array.size(), jni_find_class(env, "java/lang/Object"), nullptr); for (int j = 0; j < array.size(); j++) { Variant var = array[j]; @@ -279,7 +282,7 @@ jvalret _variant_to_jvalue(JNIEnv *env, Variant::Type p_type, const Variant *p_a } String _get_class_name(JNIEnv *env, jclass cls, bool *array) { - jclass cclass = env->FindClass("java/lang/Class"); + jclass cclass = jni_find_class(env, "java/lang/Class"); jmethodID getName = env->GetMethodID(cclass, "getName", "()Ljava/lang/String;"); jstring clsName = (jstring)env->CallObjectMethod(cls, getName); @@ -346,7 +349,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { } if (name == "java.lang.Integer" || name == "java.lang.Long") { - jclass nclass = env->FindClass("java/lang/Number"); + jclass nclass = jni_find_class(env, "java/lang/Number"); jmethodID longValue = env->GetMethodID(nclass, "longValue", "()J"); jlong ret = env->CallLongMethod(obj, longValue); return ret; @@ -386,7 +389,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { } if (name == "java.lang.Float" || name == "java.lang.Double") { - jclass nclass = env->FindClass("java/lang/Number"); + jclass nclass = jni_find_class(env, "java/lang/Number"); jmethodID doubleValue = env->GetMethodID(nclass, "doubleValue", "()D"); double ret = env->CallDoubleMethod(obj, doubleValue); return ret; @@ -509,3 +512,59 @@ Variant::Type get_jni_type(const String &p_type) { return Variant::OBJECT; } + +void setup_android_class_loader() { + // Find a known class defined in the Godot package and obtain its ClassLoader. + // This ClassLoader will be used by jni_find_class() to locate classes at runtime + // in a thread-safe manner, avoiding issues with FindClass in non-main threads. + + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL(env); + + jclass known_class = env->FindClass("org/godotengine/godot/Godot"); + ERR_FAIL_NULL(known_class); + + jclass class_class = env->FindClass("java/lang/Class"); + ERR_FAIL_NULL(class_class); + + jmethodID get_class_loader_method = env->GetMethodID(class_class, "getClassLoader", "()Ljava/lang/ClassLoader;"); + ERR_FAIL_NULL(get_class_loader_method); + + jobject class_loader = env->CallObjectMethod(known_class, get_class_loader_method); + ERR_FAIL_NULL(class_loader); + + // NOTE: Make global ref so it can be used later. + android_class_loader = env->NewGlobalRef(class_loader); + ERR_FAIL_NULL(android_class_loader); + + jclass class_loader_class = env->FindClass("java/lang/ClassLoader"); + ERR_FAIL_NULL(class_loader_class); + + load_class_method = env->GetMethodID(class_loader_class, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + if (!load_class_method) { + env->DeleteGlobalRef(android_class_loader); + android_class_loader = nullptr; + ERR_FAIL_MSG("Failed to find method ID for ClassLoader::loadClass."); + } +} + +void cleanup_android_class_loader() { + if (android_class_loader != nullptr) { + JNIEnv *env = get_jni_env(); + if (env) { + env->DeleteGlobalRef(android_class_loader); + android_class_loader = nullptr; + } + } +} + +jclass jni_find_class(JNIEnv *p_env, const char *p_class_name) { + jstring java_class_name = p_env->NewStringUTF(p_class_name); + jobject class_object = p_env->CallObjectMethod( + android_class_loader, + load_class_method, + java_class_name); + p_env->DeleteLocalRef(java_class_name); + ERR_FAIL_NULL_V_MSG(class_object, nullptr, vformat("Failed to find Java class: \"%s\".", p_class_name)); + return static_cast(class_object); +} diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h index 979c479635b..e799305a707 100644 --- a/platform/android/jni_utils.h +++ b/platform/android/jni_utils.h @@ -96,3 +96,18 @@ static inline String jstring_to_string(jstring source, JNIEnv *env = nullptr) { } return result; } + +/** + * Set up thread-safe Android ClassLoader (used by jni_find_class() below). + */ +void setup_android_class_loader(); +void cleanup_android_class_loader(); + +/** + * Thread-safe JNI class finder using Android ClassLoader. + * Works on any thread, unlike standard FindClass which may fail on non-main threads. + * @param p_env JNI environment instance. + * @param p_class_name Class name to find. + * @return jclass reference or null if not found. + */ +jclass jni_find_class(JNIEnv *p_env, const char *p_class_name);