如何从单独的线程中调用 C++ 对象上的 Java 函数
How to call a Java function on an object from C++ from a separate thread
好吧,我不能让它正常工作。我在 Android 并使用 JNI 启动大量 C++ 代码。我想将单个字符串发送回 Main Activity 以便我可以在屏幕上显示它,但我总是被卡住。经过大量工作后,我确实发现我需要先附加当前线程,因为我的 jvm 指针无效。但是现在我无法弄清楚如何从这个新线程中将我的作业用于 MainActivity。
因此,为了开始编写 C++ 代码,我这样做了:
extern "C" JNIEXPORT jstring JNICALL
Java_com_myname_mydetails_myfunction_MainActivity_startItUp(
JNIEnv* env,
_jobject *jobjecte /* this */) {
//trying to store the JVM
jint err = env->GetJavaVM(&jvm);
//start the cameras up starting with camera 1
startThings(jvm, jobjecte, env);
//return a the Starting Cameras String for the java UI
return env->NewStringUTF(hello.c_str());
}
我用这个函数将这些点传递到我的 c++ 对象中
void setJVM( JavaVM *_jvm, _jobject *_mainActivity, JNIEnv* _env){
jvm = _jvm;
mainActivity = _mainActivity;
env = _env;
}
现在在我的 C++ 领域线程中。
void myclass::mymethod(int64_t _profile){
JNIEnv *env = NULL;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // choose your JNI version
args.name = NULL; // you might want to give the java thread a name
args.group = NULL; // you might want to assign the java thread to a ThreadGroup
jvm->AttachCurrentThread(&env, &args);
jclass clazz = env->GetObjectClass(mainActivity);
当我从线程调用 mymethod 时,我得到:JNI 检测到应用程序错误:使用了无效的 jobject。好吧,公平地说,我猜如果 JVM 指针在线程之间不相同,那么 jobject 指针也不相同。但是我如何获得指向我的 Main Activity 对象的指针来调用我的 java 函数?
为了在本机代码中存储 Java 对象,您需要存储对它的 global
引用。要将 local
引用转换为全局引用,您需要调用 env->NewGlobalRef(local_object);
这将为您 return 您现在可以在任何地方使用的全局对象引用。稍后,当您使用完该对象时,您需要使用 env->DeleteGlobalRef(global_object);
清理它。如果不这样做,该对象将永远不会被 JVM 垃圾回收。
同样,env->DeleteLocalRef(local_object)
也是一个好习惯。例如,如果您这样做了:
void execute_some_code() {
jclass local_class = env->FindClass(....);
jobject local_object = env->CallObjectMethod(....);
_my_object = env->NewGlobalRef(local_object);
// MUST cleanup local references since these references were not given to you by the JVM, but rather created by you in native code.
env->DeleteLocalRef(local_object);
env->DeleteLocalRef(local_class);
}
现在要解决您的问题,请如上所述将对象存储为 global
引用。然后您可以在任何 Attached
线程上访问它。不要忘记在线程死亡之前 detach
线程(否则如果它总是 运行 则将其附加为守护进程)。
void startThings(JVM* jvm, jobject* jobject, JNIEnv* env) {
setJVM(jvm, env->NewGlobalRef(jobject), env);
// Possible other code
std::thread([]{
JNIEnv* local_env = NULL;
JavaVMAttachArgs args = {....};
int err = _jvm->AttachCurrentThread(&local_env, &args);
if (err != JNI_OK) {
return;
}
// Do stuff with _mainActivity
// Some time later in your application:
local_env->DeleteGlobalRef(_mainActivity);
_jvm->DetachCurrentThread();
}).detach();
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_myname_mydetails_myfunction_MainActivity_startItUp(
JNIEnv* env,
_jobject *jobject /* this */) {
jint err = env->GetJavaVM(&jvm);
startThings(jvm, jobject, env);
// Optional here but a good habit to know how it works
env->DeleteLocalRef(jobject);
return env->NewStringUTF(hello.c_str());
}
如果您使用的是 C++11,您实际上可以使用 std::unique_ptr
和自定义删除器来处理所有内存管理,例如删除全局引用:)
好吧,我不能让它正常工作。我在 Android 并使用 JNI 启动大量 C++ 代码。我想将单个字符串发送回 Main Activity 以便我可以在屏幕上显示它,但我总是被卡住。经过大量工作后,我确实发现我需要先附加当前线程,因为我的 jvm 指针无效。但是现在我无法弄清楚如何从这个新线程中将我的作业用于 MainActivity。
因此,为了开始编写 C++ 代码,我这样做了:
extern "C" JNIEXPORT jstring JNICALL
Java_com_myname_mydetails_myfunction_MainActivity_startItUp(
JNIEnv* env,
_jobject *jobjecte /* this */) {
//trying to store the JVM
jint err = env->GetJavaVM(&jvm);
//start the cameras up starting with camera 1
startThings(jvm, jobjecte, env);
//return a the Starting Cameras String for the java UI
return env->NewStringUTF(hello.c_str());
}
我用这个函数将这些点传递到我的 c++ 对象中
void setJVM( JavaVM *_jvm, _jobject *_mainActivity, JNIEnv* _env){
jvm = _jvm;
mainActivity = _mainActivity;
env = _env;
}
现在在我的 C++ 领域线程中。
void myclass::mymethod(int64_t _profile){
JNIEnv *env = NULL;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // choose your JNI version
args.name = NULL; // you might want to give the java thread a name
args.group = NULL; // you might want to assign the java thread to a ThreadGroup
jvm->AttachCurrentThread(&env, &args);
jclass clazz = env->GetObjectClass(mainActivity);
当我从线程调用 mymethod 时,我得到:JNI 检测到应用程序错误:使用了无效的 jobject。好吧,公平地说,我猜如果 JVM 指针在线程之间不相同,那么 jobject 指针也不相同。但是我如何获得指向我的 Main Activity 对象的指针来调用我的 java 函数?
为了在本机代码中存储 Java 对象,您需要存储对它的 global
引用。要将 local
引用转换为全局引用,您需要调用 env->NewGlobalRef(local_object);
这将为您 return 您现在可以在任何地方使用的全局对象引用。稍后,当您使用完该对象时,您需要使用 env->DeleteGlobalRef(global_object);
清理它。如果不这样做,该对象将永远不会被 JVM 垃圾回收。
同样,env->DeleteLocalRef(local_object)
也是一个好习惯。例如,如果您这样做了:
void execute_some_code() {
jclass local_class = env->FindClass(....);
jobject local_object = env->CallObjectMethod(....);
_my_object = env->NewGlobalRef(local_object);
// MUST cleanup local references since these references were not given to you by the JVM, but rather created by you in native code.
env->DeleteLocalRef(local_object);
env->DeleteLocalRef(local_class);
}
现在要解决您的问题,请如上所述将对象存储为 global
引用。然后您可以在任何 Attached
线程上访问它。不要忘记在线程死亡之前 detach
线程(否则如果它总是 运行 则将其附加为守护进程)。
void startThings(JVM* jvm, jobject* jobject, JNIEnv* env) {
setJVM(jvm, env->NewGlobalRef(jobject), env);
// Possible other code
std::thread([]{
JNIEnv* local_env = NULL;
JavaVMAttachArgs args = {....};
int err = _jvm->AttachCurrentThread(&local_env, &args);
if (err != JNI_OK) {
return;
}
// Do stuff with _mainActivity
// Some time later in your application:
local_env->DeleteGlobalRef(_mainActivity);
_jvm->DetachCurrentThread();
}).detach();
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_myname_mydetails_myfunction_MainActivity_startItUp(
JNIEnv* env,
_jobject *jobject /* this */) {
jint err = env->GetJavaVM(&jvm);
startThings(jvm, jobject, env);
// Optional here but a good habit to know how it works
env->DeleteLocalRef(jobject);
return env->NewStringUTF(hello.c_str());
}
如果您使用的是 C++11,您实际上可以使用 std::unique_ptr
和自定义删除器来处理所有内存管理,例如删除全局引用:)