mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-05 11:28:17 -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);
|
return BitmapFactory.decodeStream(stream, null, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static String getCurrentThreadName() {
|
||||||
|
return Thread.currentThread().getName();
|
||||||
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("gnustl_shared");
|
System.loadLibrary("gnustl_shared");
|
||||||
System.loadLibrary("p3dtool");
|
System.loadLibrary("p3dtool");
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "virtualFileMountAndroidAsset.h"
|
#include "virtualFileMountAndroidAsset.h"
|
||||||
#include "virtualFileSystem.h"
|
#include "virtualFileSystem.h"
|
||||||
#include "filename.h"
|
#include "filename.h"
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
#include "config_display.h"
|
#include "config_display.h"
|
||||||
// #define OPENGLES_1 #include "config_androiddisplay.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
|
* This function is called by native_app_glue to initialize the program. It
|
||||||
* simply stores the android_app object and calls main() normally.
|
* 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) {
|
void android_main(struct android_app* app) {
|
||||||
panda_android_app = app;
|
panda_android_app = app;
|
||||||
|
|
||||||
// Attach the current thread to the JVM.
|
// Attach the app thread to the Java VM.
|
||||||
JNIEnv *env;
|
JNIEnv *env;
|
||||||
ANativeActivity* activity = app->activity;
|
ANativeActivity* activity = app->activity;
|
||||||
int status = activity->vm->AttachCurrentThread(&env, NULL);
|
int status = activity->vm->AttachCurrentThread(&env, nullptr);
|
||||||
if (status < 0 || env == NULL) {
|
if (status < 0 || env == nullptr) {
|
||||||
android_cat.error() << "Failed to attach thread to JVM!\n";
|
android_cat.error() << "Failed to attach thread to JVM!\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the data directory.
|
|
||||||
jclass activity_class = env->GetObjectClass(activity->clazz);
|
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;");
|
jmethodID get_appinfo = env->GetMethodID(activity_class, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
|
||||||
|
|
||||||
jobject appinfo = env->CallObjectMethod(activity->clazz, get_appinfo);
|
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.
|
// 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);
|
jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
|
||||||
|
|
||||||
const char* apk_path;
|
const char* apk_path;
|
||||||
|
@ -49,8 +49,9 @@ init_libandroid() {
|
|||||||
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||||
init_libandroid();
|
init_libandroid();
|
||||||
|
|
||||||
JNIEnv *env = get_jni_env();
|
Thread *thread = Thread::get_current_thread();
|
||||||
assert(env != NULL);
|
JNIEnv *env = thread->get_jni_env();
|
||||||
|
nassertr(env != nullptr, -1);
|
||||||
|
|
||||||
jni_PandaActivity = env->FindClass("org/panda3d/android/PandaActivity");
|
jni_PandaActivity = env->FindClass("org/panda3d/android/PandaActivity");
|
||||||
jni_PandaActivity = (jclass) env->NewGlobalRef(jni_PandaActivity);
|
jni_PandaActivity = (jclass) env->NewGlobalRef(jni_PandaActivity);
|
||||||
@ -75,7 +76,9 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
|||||||
* references.
|
* references.
|
||||||
*/
|
*/
|
||||||
void JNI_OnUnload(JavaVM *jvm, void *reserved) {
|
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_PandaActivity);
|
||||||
env->DeleteGlobalRef(jni_BitmapFactory_Options);
|
env->DeleteGlobalRef(jni_BitmapFactory_Options);
|
||||||
|
@ -76,7 +76,14 @@ Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
streampos pos = _file->tellg();
|
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,
|
jobject opts = _env->CallStaticObjectMethod(jni_PandaActivity,
|
||||||
jni_PandaActivity_readBitmapSize,
|
jni_PandaActivity_readBitmapSize,
|
||||||
(jlong) _file);
|
(jlong) _file);
|
||||||
|
@ -194,40 +194,3 @@ get_config_express() {
|
|||||||
static DConfig config_express;
|
static DConfig config_express;
|
||||||
return 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 "executionEnvironment.h"
|
||||||
#include "lineStream.h"
|
#include "lineStream.h"
|
||||||
|
|
||||||
#ifdef ANDROID
|
|
||||||
#include <jni.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||||
NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||||
NotifyCategoryDecl(clock, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
NotifyCategoryDecl(clock, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
|
||||||
@ -65,9 +61,4 @@ END_PUBLISH
|
|||||||
|
|
||||||
extern EXPCL_PANDAEXPRESS void init_libexpress();
|
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__ */
|
#endif /* __CONFIG_UTIL_H__ */
|
||||||
|
@ -300,6 +300,17 @@ prepare_for_exit() {
|
|||||||
ThreadImpl::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
|
* 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.
|
* internally by the PStatClient; you should not need to call this directly.
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
#include "pnotify.h"
|
#include "pnotify.h"
|
||||||
#include "config_pipeline.h"
|
#include "config_pipeline.h"
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
typedef struct _JNIEnv JNIEnv;
|
||||||
|
#endif
|
||||||
|
|
||||||
class Mutex;
|
class Mutex;
|
||||||
class ReMutex;
|
class ReMutex;
|
||||||
class MutexDebug;
|
class MutexDebug;
|
||||||
@ -128,6 +132,10 @@ public:
|
|||||||
INLINE void set_pstats_callback(PStatsCallback *pstats_callback);
|
INLINE void set_pstats_callback(PStatsCallback *pstats_callback);
|
||||||
INLINE PStatsCallback *get_pstats_callback() const;
|
INLINE PStatsCallback *get_pstats_callback() const;
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
INLINE JNIEnv *get_jni_env() const;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void init_main_thread();
|
static void init_main_thread();
|
||||||
static void init_external_thread();
|
static void init_external_thread();
|
||||||
|
@ -21,6 +21,9 @@ ThreadPosixImpl(Thread *parent_obj) :
|
|||||||
_joinable = false;
|
_joinable = false;
|
||||||
_detached = false;
|
_detached = false;
|
||||||
_status = S_new;
|
_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);
|
int result = pthread_setspecific(_pt_ptr_index, thread);
|
||||||
nassertv(result == 0);
|
nassertv(result == 0);
|
||||||
|
#ifdef ANDROID
|
||||||
|
bind_java_thread();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,3 +118,13 @@ yield() {
|
|||||||
INLINE void ThreadPosixImpl::
|
INLINE void ThreadPosixImpl::
|
||||||
consider_yield() {
|
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
|
#ifdef ANDROID
|
||||||
#include "config_express.h"
|
#include "config_express.h"
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
static JavaVM *java_vm = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
|
pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
|
||||||
@ -183,6 +185,53 @@ get_unique_id() const {
|
|||||||
return strm.str();
|
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.
|
* The entry point of each thread.
|
||||||
*/
|
*/
|
||||||
@ -209,14 +258,7 @@ root_func(void *data) {
|
|||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
// Attach the Java VM to allow calling Java functions in this thread.
|
// Attach the Java VM to allow calling Java functions in this thread.
|
||||||
JavaVM *jvm = get_java_vm();
|
self->attach_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;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
self->_parent_obj->thread_main();
|
self->_parent_obj->thread_main();
|
||||||
@ -238,8 +280,10 @@ root_func(void *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
if (env != NULL) {
|
// We cannot let the thread end without detaching it.
|
||||||
jvm->DetachCurrentThread();
|
if (self->_jni_env != nullptr) {
|
||||||
|
java_vm->DetachCurrentThread();
|
||||||
|
self->_jni_env = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -276,4 +320,17 @@ init_pt_ptr_index() {
|
|||||||
nassertv(result == 0);
|
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
|
#endif // THREAD_POSIX_IMPL
|
||||||
|
@ -25,6 +25,10 @@
|
|||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
typedef struct _JNIEnv JNIEnv;
|
||||||
|
#endif
|
||||||
|
|
||||||
class Thread;
|
class Thread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +57,12 @@ public:
|
|||||||
INLINE static void yield();
|
INLINE static void yield();
|
||||||
INLINE static void consider_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:
|
private:
|
||||||
static void *root_func(void *data);
|
static void *root_func(void *data);
|
||||||
static void init_pt_ptr_index();
|
static void init_pt_ptr_index();
|
||||||
@ -72,6 +82,10 @@ private:
|
|||||||
bool _detached;
|
bool _detached;
|
||||||
PStatus _status;
|
PStatus _status;
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
JNIEnv *_jni_env;
|
||||||
|
#endif
|
||||||
|
|
||||||
static pthread_key_t _pt_ptr_index;
|
static pthread_key_t _pt_ptr_index;
|
||||||
static bool _got_pt_ptr_index;
|
static bool _got_pt_ptr_index;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user