mirror of
https://github.com/amwatson/CitraVR.git
synced 2024-09-20 03:11:40 +02:00
init the classloader on the Java thread as a speculative fix for SkilarBabcock's crash
This commit is contained in:
parent
a2a360d4bb
commit
d0a85182c2
6 changed files with 98 additions and 63 deletions
|
@ -39,6 +39,7 @@ add_library(citra-android SHARED
|
|||
vr/layers/PassthroughLayer.cpp
|
||||
vr/layers/UILayer.cpp
|
||||
vr/utils/JniUtils.cpp
|
||||
vr/utils/JniClassNames.cpp
|
||||
vr/utils/MessageQueue.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -167,7 +167,8 @@ UILayer::UILayer(const std::string& className, const XrVector3f&& position,
|
|||
, mEnv(env) {
|
||||
const int32_t initializationStatus = Init(className, activityObject, position, session);
|
||||
if (initializationStatus < 0) {
|
||||
FAIL("Could not initialize UILayer(%s) -- error '%d'", className.c_str(), initializationStatus);
|
||||
FAIL("Could not initialize UILayer(%s) -- error '%d'", className.c_str(),
|
||||
initializationStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
47
src/android/app/src/main/jni/vr/utils/JniClassNames.cpp
Normal file
47
src/android/app/src/main/jni/vr/utils/JniClassNames.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "JniClassNames.h"
|
||||
|
||||
#include "LogUtils.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace VR {
|
||||
namespace JniGlobalRef {
|
||||
jmethodID gFindClassMethodID = nullptr;
|
||||
jobject gClassLoader = nullptr;
|
||||
} // namespace JniGlobalRef
|
||||
} // namespace VR
|
||||
|
||||
void VR::JNI::InitJNI(JNIEnv* jni, jobject activityObject) {
|
||||
assert(jni != nullptr);
|
||||
const jclass activityClass = jni->GetObjectClass(activityObject);
|
||||
if (activityClass == nullptr) { FAIL("Failed to get activity class"); }
|
||||
|
||||
// Get the getClassLoader method ID
|
||||
const jmethodID getClassLoaderMethod =
|
||||
jni->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
if (getClassLoaderMethod == nullptr) { FAIL("Failed to get getClassLoader method ID"); }
|
||||
|
||||
// Call getClassLoader of the activity object to obtain the class loader
|
||||
const jobject classLoaderObject = jni->CallObjectMethod(activityObject, getClassLoaderMethod);
|
||||
if (classLoaderObject == nullptr) { FAIL("Failed to get class loader object"); }
|
||||
|
||||
JniGlobalRef::gClassLoader = jni->NewGlobalRef(classLoaderObject);
|
||||
|
||||
// Step 3: Cache the findClass method ID
|
||||
jclass classLoaderClass = jni->FindClass("java/lang/ClassLoader");
|
||||
if (classLoaderClass == nullptr) { FAIL("Failed to find class loader class"); }
|
||||
JniGlobalRef::gFindClassMethodID =
|
||||
jni->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
if (JniGlobalRef::gFindClassMethodID == nullptr) { FAIL("Failed to get findClass method ID"); }
|
||||
|
||||
// Cleanup local references
|
||||
jni->DeleteLocalRef(activityClass);
|
||||
jni->DeleteLocalRef(classLoaderClass);
|
||||
}
|
||||
|
||||
void VR::JNI::CleanupJNI(JNIEnv* jni) {
|
||||
assert(jni != nullptr);
|
||||
if (JniGlobalRef::gClassLoader != nullptr) { jni->DeleteGlobalRef(JniGlobalRef::gClassLoader); }
|
||||
JniGlobalRef::gClassLoader = nullptr;
|
||||
JniGlobalRef::gFindClassMethodID = nullptr;
|
||||
}
|
18
src/android/app/src/main/jni/vr/utils/JniClassNames.h
Normal file
18
src/android/app/src/main/jni/vr/utils/JniClassNames.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace VR {
|
||||
namespace JniGlobalRef {
|
||||
extern jmethodID gFindClassMethodID;
|
||||
extern jobject gClassLoader;
|
||||
|
||||
} // namespace JniGlobalRef
|
||||
|
||||
namespace JNI {
|
||||
// Called during JNI_OnLoad
|
||||
void InitJNI(JNIEnv* env, jobject activityObject);
|
||||
// Called during JNI_OnUnload
|
||||
void CleanupJNI(JNIEnv* env);
|
||||
} // namespace JNI
|
||||
} // namespace VR
|
|
@ -12,74 +12,39 @@ License : Licensed under GPLv3 or any later version.
|
|||
|
||||
#include "JniUtils.h"
|
||||
|
||||
#include "JniClassNames.h"
|
||||
#include "LogUtils.h"
|
||||
|
||||
jclass JniUtils::GetGlobalClassReference(JNIEnv* jni, jobject activityObject,
|
||||
jclass JniUtils::GetGlobalClassReference(JNIEnv* env, jobject activityObject,
|
||||
const std::string& className) {
|
||||
// First, get the class object of the activity to get its class loader
|
||||
const jclass activityClass = jni->GetObjectClass(activityObject);
|
||||
if (activityClass == nullptr) {
|
||||
ALOGE("Failed to get activity class");
|
||||
// Convert dot ('.') to slash ('/') in class name (Java uses dots, JNI uses slashes for class
|
||||
// names)
|
||||
std::string correctedClassName = className;
|
||||
std::replace(correctedClassName.begin(), correctedClassName.end(), '.', '/');
|
||||
|
||||
// Convert std::string to jstring
|
||||
jstring classNameJString = env->NewStringUTF(correctedClassName.c_str());
|
||||
|
||||
// Use the global class loader to find the class
|
||||
jclass clazz = static_cast<jclass>(env->CallObjectMethod(
|
||||
VR::JniGlobalRef::gClassLoader, VR::JniGlobalRef::gFindClassMethodID, classNameJString));
|
||||
if (clazz == nullptr) {
|
||||
// Class not found
|
||||
ALOGE("Class not found: %s", correctedClassName.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the getClassLoader method ID
|
||||
const jmethodID getClassLoaderMethod =
|
||||
jni->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
if (getClassLoaderMethod == nullptr) {
|
||||
ALOGE("Failed to get getClassLoader method ID");
|
||||
return nullptr;
|
||||
// Clean up the local reference to the class name jstring
|
||||
env->DeleteLocalRef(classNameJString);
|
||||
|
||||
// Check for exceptions and handle them. This is crucial to prevent crashes due to uncaught
|
||||
// exceptions.
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
return nullptr; // Class not found or other issue
|
||||
}
|
||||
|
||||
// Call getClassLoader of the activity object to obtain the class loader
|
||||
const jobject classLoaderObject = jni->CallObjectMethod(activityObject, getClassLoaderMethod);
|
||||
if (classLoaderObject == nullptr) {
|
||||
ALOGE("Failed to get class loader object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the class loader class
|
||||
const jclass classLoaderClass = jni->FindClass("java/lang/ClassLoader");
|
||||
if (classLoaderClass == nullptr) {
|
||||
ALOGE("Failed to get class loader class");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the findClass method ID from the class loader class
|
||||
const jmethodID findClassMethod =
|
||||
jni->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
if (findClassMethod == nullptr) {
|
||||
ALOGE("Failed to get findClass method ID");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Convert the class name string to a jstring
|
||||
const jstring javaClassName = jni->NewStringUTF(className.c_str());
|
||||
if (javaClassName == nullptr) {
|
||||
ALOGE("Failed to convert class name to jstring");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Call findClass on the class loader object with the class name
|
||||
const jclass classToFind = static_cast<jclass>(
|
||||
jni->CallObjectMethod(classLoaderObject, findClassMethod, javaClassName));
|
||||
|
||||
// Clean up local references
|
||||
jni->DeleteLocalRef(activityClass);
|
||||
jni->DeleteLocalRef(classLoaderObject);
|
||||
jni->DeleteLocalRef(classLoaderClass);
|
||||
jni->DeleteLocalRef(javaClassName);
|
||||
|
||||
if (classToFind == nullptr) {
|
||||
// Handle error (Class not found)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a global reference to the class
|
||||
const jclass globalClassRef = reinterpret_cast<jclass>(jni->NewGlobalRef(classToFind));
|
||||
|
||||
// Clean up the local reference of the class
|
||||
jni->DeleteLocalRef(classToFind);
|
||||
|
||||
return globalClassRef;
|
||||
// Return a global reference to the class
|
||||
return static_cast<jclass>(env->NewGlobalRef(clazz));
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ License : Licensed under GPLv3 or any later version.
|
|||
#include "vr_settings.h"
|
||||
|
||||
#include "utils/Common.h"
|
||||
#include "utils/JniClassNames.h"
|
||||
#include "utils/MessageQueue.h"
|
||||
#include "utils/SyspropUtils.h"
|
||||
#include "utils/XrMath.h"
|
||||
|
@ -1126,6 +1127,7 @@ Java_org_citra_citra_1emu_vr_VrActivity_nativeOnCreate(JNIEnv* env, jobject thiz
|
|||
// time to first frame.
|
||||
gOnCreateStartTime = std::chrono::steady_clock::now();
|
||||
|
||||
VR::JNI::InitJNI(env, thiz);
|
||||
JavaVM* jvm;
|
||||
env->GetJavaVM(&jvm);
|
||||
auto ret = VRAppHandle(new VRAppThread(jvm, env, thiz)).l;
|
||||
|
@ -1138,6 +1140,7 @@ Java_org_citra_citra_1emu_vr_VrActivity_nativeOnDestroy(JNIEnv* env, jobject thi
|
|||
|
||||
ALOGI("nativeOnDestroy {}", static_cast<long>(handle));
|
||||
if (handle != 0) { delete VRAppHandle(handle).p; }
|
||||
VR::JNI::CleanupJNI(env);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
|
|
Loading…
Reference in a new issue