mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-05 03:15:07 -04:00
android: properly support multiple Java threads
This commit is contained in:
parent
2dba9357bb
commit
c1fccd311b
@ -41,6 +41,10 @@ public class PandaActivity extends NativeActivity {
|
||||
return BitmapFactory.decodeStream(stream, null, options);
|
||||
}
|
||||
|
||||
protected static String getCurrentThreadName() {
|
||||
return Thread.currentThread().getName();
|
||||
}
|
||||
|
||||
static {
|
||||
System.loadLibrary("gnustl_shared");
|
||||
System.loadLibrary("p3dtool");
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "virtualFileMountAndroidAsset.h"
|
||||
#include "virtualFileSystem.h"
|
||||
#include "filename.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include "config_display.h"
|
||||
// #define OPENGLES_1 #include "config_androiddisplay.h"
|
||||
@ -29,21 +30,44 @@ extern int main(int argc, char **argv);
|
||||
/**
|
||||
* This function is called by native_app_glue to initialize the program. It
|
||||
* simply stores the android_app object and calls main() normally.
|
||||
*
|
||||
* Note that this does not run in the main thread, but in a thread created
|
||||
* specifically for this activity by android_native_app_glue.
|
||||
*/
|
||||
void android_main(struct android_app* app) {
|
||||
panda_android_app = app;
|
||||
|
||||
// Attach the current thread to the JVM.
|
||||
// Attach the app thread to the Java VM.
|
||||
JNIEnv *env;
|
||||
ANativeActivity* activity = app->activity;
|
||||
int status = activity->vm->AttachCurrentThread(&env, NULL);
|
||||
if (status < 0 || env == NULL) {
|
||||
int status = activity->vm->AttachCurrentThread(&env, nullptr);
|
||||
if (status < 0 || env == nullptr) {
|
||||
android_cat.error() << "Failed to attach thread to JVM!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the data directory.
|
||||
jclass activity_class = env->GetObjectClass(activity->clazz);
|
||||
|
||||
// Get the current Java thread name. This just helps with debugging.
|
||||
jmethodID methodID = env->GetStaticMethodID(activity_class, "getCurrentThreadName", "()Ljava/lang/String;");
|
||||
jstring jthread_name = (jstring) env->CallStaticObjectMethod(activity_class, methodID);
|
||||
|
||||
string thread_name;
|
||||
if (jthread_name != nullptr) {
|
||||
const char *c_str = env->GetStringUTFChars(jthread_name, nullptr);
|
||||
thread_name.assign(c_str);
|
||||
env->ReleaseStringUTFChars(jthread_name, c_str);
|
||||
}
|
||||
|
||||
// Before we make any Panda calls, we must make the thread known to Panda.
|
||||
// This will also cause the JNIEnv pointer to be stored on the thread.
|
||||
// Note that we must keep a reference to this thread around.
|
||||
PT(Thread) current_thread = Thread::bind_thread(thread_name, "android_app");
|
||||
|
||||
android_cat.info()
|
||||
<< "New native activity started on " << *current_thread << "\n";
|
||||
|
||||
// Fetch the data directory.
|
||||
jmethodID get_appinfo = env->GetMethodID(activity_class, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
|
||||
|
||||
jobject appinfo = env->CallObjectMethod(activity->clazz, get_appinfo);
|
||||
@ -77,7 +101,7 @@ void android_main(struct android_app* app) {
|
||||
}
|
||||
|
||||
// Get the path to the APK.
|
||||
jmethodID methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
|
||||
methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
|
||||
jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
|
||||
|
||||
const char* apk_path;
|
||||
|
@ -49,8 +49,9 @@ init_libandroid() {
|
||||
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
init_libandroid();
|
||||
|
||||
JNIEnv *env = get_jni_env();
|
||||
assert(env != NULL);
|
||||
Thread *thread = Thread::get_current_thread();
|
||||
JNIEnv *env = thread->get_jni_env();
|
||||
nassertr(env != nullptr, -1);
|
||||
|
||||
jni_PandaActivity = env->FindClass("org/panda3d/android/PandaActivity");
|
||||
jni_PandaActivity = (jclass) env->NewGlobalRef(jni_PandaActivity);
|
||||
@ -75,7 +76,9 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
* references.
|
||||
*/
|
||||
void JNI_OnUnload(JavaVM *jvm, void *reserved) {
|
||||
JNIEnv *env = get_jni_env();
|
||||
Thread *thread = Thread::get_current_thread();
|
||||
JNIEnv *env = thread->get_jni_env();
|
||||
nassertv(env != nullptr);
|
||||
|
||||
env->DeleteGlobalRef(jni_PandaActivity);
|
||||
env->DeleteGlobalRef(jni_BitmapFactory_Options);
|
||||
|
@ -76,7 +76,14 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
|
||||
}
|
||||
|
||||
streampos pos = _file->tellg();
|
||||
_env = get_jni_env();
|
||||
|
||||
Thread *current_thread = Thread::get_current_thread();
|
||||
_env = current_thread->get_jni_env();
|
||||
nassertd(_env != nullptr) {
|
||||
_is_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
jobject opts = _env->CallStaticObjectMethod(jni_PandaActivity,
|
||||
jni_PandaActivity_readBitmapSize,
|
||||
(jlong) _file);
|
||||
|
@ -194,40 +194,3 @@ get_config_express() {
|
||||
static DConfig config_express;
|
||||
return config_express;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
static JavaVM *panda_jvm = NULL;
|
||||
|
||||
/**
|
||||
* Called by Java when loading this library.
|
||||
*/
|
||||
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
panda_jvm = jvm;
|
||||
return JNI_VERSION_1_4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the JavaVM object.
|
||||
*/
|
||||
JavaVM *get_java_vm() {
|
||||
nassertr(panda_jvm != NULL, NULL);
|
||||
return panda_jvm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JNIEnv object for the current thread. If it doesn't already
|
||||
* exist, attaches the JVM to this thread.
|
||||
*/
|
||||
JNIEnv *get_jni_env() {
|
||||
nassertr(panda_jvm != NULL, NULL);
|
||||
JNIEnv *env = NULL;
|
||||
int status = panda_jvm->GetEnv((void**) &env, JNI_VERSION_1_4);
|
||||
|
||||
if (status < 0 || env == NULL) {
|
||||
express_cat.error() << "JVM is not available in this thread!\n";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
#endif
|
||||
|
@ -28,10 +28,6 @@
|
||||
#include "executionEnvironment.h"
|
||||
#include "lineStream.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||
NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||
NotifyCategoryDecl(clock, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||
@ -65,9 +61,4 @@ END_PUBLISH
|
||||
|
||||
extern EXPCL_PANDAEXPRESS void init_libexpress();
|
||||
|
||||
#ifdef ANDROID
|
||||
extern EXPCL_PANDAEXPRESS JavaVM *get_java_vm();
|
||||
extern EXPCL_PANDAEXPRESS JNIEnv *get_jni_env();
|
||||
#endif
|
||||
|
||||
#endif /* __CONFIG_UTIL_H__ */
|
||||
|
@ -300,6 +300,17 @@ prepare_for_exit() {
|
||||
ThreadImpl::prepare_for_exit();
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
/**
|
||||
* Enables interaction with the Java VM on Android. Returns null if the
|
||||
* thread is not attached to the Java VM (or bind_thread was not called).
|
||||
*/
|
||||
INLINE JNIEnv *Thread::
|
||||
get_jni_env() const {
|
||||
return _impl.get_jni_env();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Stores a PStats index to be associated with this thread. This is used
|
||||
* internally by the PStatClient; you should not need to call this directly.
|
||||
|
@ -23,6 +23,10 @@
|
||||
#include "pnotify.h"
|
||||
#include "config_pipeline.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
typedef struct _JNIEnv JNIEnv;
|
||||
#endif
|
||||
|
||||
class Mutex;
|
||||
class ReMutex;
|
||||
class MutexDebug;
|
||||
@ -128,6 +132,10 @@ public:
|
||||
INLINE void set_pstats_callback(PStatsCallback *pstats_callback);
|
||||
INLINE PStatsCallback *get_pstats_callback() const;
|
||||
|
||||
#ifdef ANDROID
|
||||
INLINE JNIEnv *get_jni_env() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
static void init_main_thread();
|
||||
static void init_external_thread();
|
||||
|
@ -21,6 +21,9 @@ ThreadPosixImpl(Thread *parent_obj) :
|
||||
_joinable = false;
|
||||
_detached = false;
|
||||
_status = S_new;
|
||||
#ifdef ANDROID
|
||||
_jni_env = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,6 +63,9 @@ bind_thread(Thread *thread) {
|
||||
}
|
||||
int result = pthread_setspecific(_pt_ptr_index, thread);
|
||||
nassertv(result == 0);
|
||||
#ifdef ANDROID
|
||||
bind_java_thread();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,3 +118,13 @@ yield() {
|
||||
INLINE void ThreadPosixImpl::
|
||||
consider_yield() {
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
/**
|
||||
* Returns the JNIEnv object for the current thread.
|
||||
*/
|
||||
INLINE JNIEnv *ThreadPosixImpl::
|
||||
get_jni_env() const {
|
||||
return _jni_env;
|
||||
}
|
||||
#endif
|
||||
|
@ -24,6 +24,8 @@
|
||||
#ifdef ANDROID
|
||||
#include "config_express.h"
|
||||
#include <jni.h>
|
||||
|
||||
static JavaVM *java_vm = nullptr;
|
||||
#endif
|
||||
|
||||
pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
|
||||
@ -183,6 +185,53 @@ get_unique_id() const {
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
/**
|
||||
* Attaches the thread to the Java virtual machine. If this returns true, a
|
||||
* JNIEnv pointer can be acquired using get_jni_env().
|
||||
*/
|
||||
bool ThreadPosixImpl::
|
||||
attach_java_vm() {
|
||||
JNIEnv *env;
|
||||
string thread_name = _parent_obj->get_name();
|
||||
JavaVMAttachArgs args;
|
||||
args.version = JNI_VERSION_1_2;
|
||||
args.name = thread_name.c_str();
|
||||
args.group = nullptr;
|
||||
if (java_vm->AttachCurrentThread(&env, &args) != 0) {
|
||||
thread_cat.error()
|
||||
<< "Failed to attach Java VM to thread "
|
||||
<< _parent_obj->get_name() << "!\n";
|
||||
_jni_env = nullptr;
|
||||
return false;
|
||||
}
|
||||
_jni_env = env;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the Panda thread to the current thread, assuming that the current
|
||||
* thread is already a valid attached Java thread. Called by JNI_OnLoad.
|
||||
*/
|
||||
void ThreadPosixImpl::
|
||||
bind_java_thread() {
|
||||
Thread *thread = Thread::get_current_thread();
|
||||
nassertv(thread != nullptr);
|
||||
|
||||
// Get the JNIEnv for this Java thread, and store it on the corresponding
|
||||
// Panda thread object.
|
||||
JNIEnv *env;
|
||||
if (java_vm->GetEnv((void **)&env, JNI_VERSION_1_4) == JNI_OK) {
|
||||
nassertv(thread->_impl._jni_env == nullptr || thread->_impl._jni_env == env);
|
||||
thread->_impl._jni_env = env;
|
||||
} else {
|
||||
thread_cat->error()
|
||||
<< "Called bind_java_thread() on thread "
|
||||
<< *thread << ", which is not attached to Java VM!\n";
|
||||
}
|
||||
}
|
||||
#endif // ANDROID
|
||||
|
||||
/**
|
||||
* The entry point of each thread.
|
||||
*/
|
||||
@ -209,14 +258,7 @@ root_func(void *data) {
|
||||
|
||||
#ifdef ANDROID
|
||||
// Attach the Java VM to allow calling Java functions in this thread.
|
||||
JavaVM *jvm = get_java_vm();
|
||||
JNIEnv *env;
|
||||
if (jvm == NULL || jvm->AttachCurrentThread(&env, NULL) != 0) {
|
||||
thread_cat.error()
|
||||
<< "Failed to attach Java VM to thread "
|
||||
<< self->_parent_obj->get_name() << "!\n";
|
||||
env = NULL;
|
||||
}
|
||||
self->attach_java_vm();
|
||||
#endif
|
||||
|
||||
self->_parent_obj->thread_main();
|
||||
@ -238,8 +280,10 @@ root_func(void *data) {
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
if (env != NULL) {
|
||||
jvm->DetachCurrentThread();
|
||||
// We cannot let the thread end without detaching it.
|
||||
if (self->_jni_env != nullptr) {
|
||||
java_vm->DetachCurrentThread();
|
||||
self->_jni_env = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -276,4 +320,17 @@ init_pt_ptr_index() {
|
||||
nassertv(result == 0);
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
/**
|
||||
* Called by Java when loading this library from the Java virtual machine.
|
||||
*/
|
||||
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
// Store the JVM pointer globally.
|
||||
java_vm = jvm;
|
||||
|
||||
ThreadPosixImpl::bind_java_thread();
|
||||
return JNI_VERSION_1_4;
|
||||
}
|
||||
#endif // ANDROID
|
||||
|
||||
#endif // THREAD_POSIX_IMPL
|
||||
|
@ -25,6 +25,10 @@
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
typedef struct _JNIEnv JNIEnv;
|
||||
#endif
|
||||
|
||||
class Thread;
|
||||
|
||||
/**
|
||||
@ -53,6 +57,12 @@ public:
|
||||
INLINE static void yield();
|
||||
INLINE static void consider_yield();
|
||||
|
||||
#ifdef ANDROID
|
||||
INLINE JNIEnv *get_jni_env() const;
|
||||
bool attach_java_vm();
|
||||
static void bind_java_thread();
|
||||
#endif
|
||||
|
||||
private:
|
||||
static void *root_func(void *data);
|
||||
static void init_pt_ptr_index();
|
||||
@ -72,6 +82,10 @@ private:
|
||||
bool _detached;
|
||||
PStatus _status;
|
||||
|
||||
#ifdef ANDROID
|
||||
JNIEnv *_jni_env;
|
||||
#endif
|
||||
|
||||
static pthread_key_t _pt_ptr_index;
|
||||
static bool _got_pt_ptr_index;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user