如何从单独的线程中调用 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 和自定义删除器来处理所有内存管理,例如删除全局引用:)