mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-22 10:21:57 -05:00
This was a regression from #7435 which introduced threads and caused JNI to misbehave and fail to load our expected classes. Provide a workaround based on the description in https://stackoverflow.com/a/16302771 which stores a main thread's class loader and uses that in neighbouring threads.
This commit is contained in:
parent
20496b0390
commit
7e769ed662
6 changed files with 83 additions and 1 deletions
|
@ -21,6 +21,7 @@
|
|||
- Fix: [#6191] OpenRCT2 fails to run when the path has an emoji in it.
|
||||
- Fix: [#7439] Placement messages have mixed strings
|
||||
- Fix: [#7473] Disabling sound effects also disables "Disable audio on focus loss".
|
||||
- Fix: [#7536] Android builds fail to start.
|
||||
- Fix: [#7689] Deleting 0-tile maze gives a MONEY32_UNDEFINED (negative) refund.
|
||||
- Fix: [#7828] Copied entrances and exits stay when demolishing ride.
|
||||
- Fix: [#7945] Client IP address is logged as `(null)` in server logs.
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
|
||||
# include <SDL.h>
|
||||
# include <dlfcn.h>
|
||||
# include <jni.h>
|
||||
# include <openrct2/common.h>
|
||||
# include <openrct2/core/String.hpp>
|
||||
# include <openrct2/platform/platform.h>
|
||||
# include <openrct2/ui/UiContext.h>
|
||||
# include <sstream>
|
||||
# include <stdexcept>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
# include "../platform/platform.h"
|
||||
# include "IStream.hpp"
|
||||
# include "Zip.h"
|
||||
|
||||
|
@ -26,7 +27,7 @@ public:
|
|||
// retrieve the JNI environment.
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
|
||||
jclass jniClass = env->FindClass("website/openrct2/ZipArchive");
|
||||
jclass jniClass = platform_android_find_class(env, "website/openrct2/ZipArchive");
|
||||
jmethodID constructor = env->GetMethodID(jniClass, "<init>", "(Ljava/lang/String;)V");
|
||||
|
||||
jstring jniPath = env->NewStringUTF(path.data());
|
||||
|
|
|
@ -62,4 +62,60 @@ bool platform_get_steam_path(utf8* outPath, size_t outSize)
|
|||
return false;
|
||||
}
|
||||
|
||||
AndroidClassLoader::AndroidClassLoader()
|
||||
{
|
||||
log_info("Obtaining JNI class loader");
|
||||
|
||||
// This is a workaround to be able to call JNI's ClassLoader from non-main
|
||||
// thread, based on https://stackoverflow.com/a/16302771
|
||||
|
||||
// Apparently it's OK to use it from across different thread, but JNI
|
||||
// only looks for ClassLoader in the _current_ thread and fails to find
|
||||
// it when searched for from a native library's non-main thread.
|
||||
|
||||
// The solution below works by obtaining a ClassLoader reference in main
|
||||
// thread and caching it for future use from any thread, instead of using
|
||||
// it via env->FindClass(). ClassLoader itself is abstract, so we cannot
|
||||
// create it directly; instead we take an arbitrary class and call
|
||||
// getClassLoader() on it to create a reference that way.
|
||||
|
||||
// If we're here, SDL's JNI_OnLoad has already been called and set env
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
|
||||
// Take an arbitrary class. While the class does not really matter, it
|
||||
// makes sense to use one that's most likely already loaded and is unlikely
|
||||
// to be removed from code.
|
||||
auto randomClass = env->FindClass("website/openrct2/MainActivity");
|
||||
jclass classClass = env->GetObjectClass(randomClass);
|
||||
|
||||
// Get its class loader
|
||||
auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
|
||||
auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
|
||||
// Store the class loader and its findClass method for future use
|
||||
_classLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod));
|
||||
_findClassMethod = env->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
}
|
||||
|
||||
AndroidClassLoader::~AndroidClassLoader()
|
||||
{
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
env->DeleteGlobalRef(_classLoader);
|
||||
}
|
||||
|
||||
jobject AndroidClassLoader::_classLoader;
|
||||
jmethodID AndroidClassLoader::_findClassMethod;
|
||||
|
||||
static std::shared_ptr<AndroidClassLoader> acl;
|
||||
|
||||
void platform_android_init_class_loader()
|
||||
{
|
||||
acl = std::make_shared<AndroidClassLoader>();
|
||||
}
|
||||
|
||||
jclass platform_android_find_class(JNIEnv* env, const char* name)
|
||||
{
|
||||
return static_cast<jclass>(
|
||||
env->CallObjectMethod(AndroidClassLoader::_classLoader, AndroidClassLoader::_findClassMethod, env->NewStringUTF(name)));
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -212,6 +212,10 @@ void core_init()
|
|||
{
|
||||
initialised = true;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
platform_android_init_class_loader();
|
||||
#endif // __ANDROID__
|
||||
|
||||
platform_ticks_init();
|
||||
bitcount_init();
|
||||
mask_init();
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
# include <jni.h>
|
||||
#endif // __ANDROID__
|
||||
|
||||
struct TTFFontDescriptor;
|
||||
struct rct2_install_info;
|
||||
|
||||
|
@ -160,4 +164,18 @@ void macos_disallow_automatic_window_tabbing();
|
|||
utf8* macos_str_decomp_to_precomp(utf8* input);
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
class AndroidClassLoader
|
||||
{
|
||||
public:
|
||||
AndroidClassLoader();
|
||||
~AndroidClassLoader();
|
||||
static jobject _classLoader;
|
||||
static jmethodID _findClassMethod;
|
||||
};
|
||||
|
||||
void platform_android_init_class_loader();
|
||||
jclass platform_android_find_class(JNIEnv* env, const char* name);
|
||||
#endif // __ANDROID__
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue