【问题标题】:How to implement the Observer pattern in JNI如何在 JNI 中实现观察者模式
【发布时间】:2013-11-28 15:45:06
【问题描述】:

我最近不得不用 Java 封装一个 C/C++ 库。其中一种方法接受一个函数作为参数。这基本上是观察者(又名监听者)模式:

void setLogFunction(const std::function<void(std::string&, std::string&)> &callback)
{
  _callback = callback;
}

在 Java 端,您不能传递函数,但可以使用 log() 方法传递对象。

interface Observer {
  public void log(String prefix, String message);
}

class MyLibrary {
  public MyLibrary() {
    initCpp();
  }
  public native void initCpp();
  ...
  public native void setObserver(Observer observer);
}

如何在 JNI 中实现 setObserver()?

【问题讨论】:

    标签: java java-native-interface observer-pattern


    【解决方案1】:

    我花了很长时间才实施这个解决方案。我不得不从互联网上获取信息。这里是:

    //Optional, this is just so that I can retrieve the C++ MyLibrary instance
    //easily from the Java.
    JNIEXPORT void JNICALL Java_MyLibrary_initCpp(JNIEnv* env, jobject javaMyLibrary) {
        //Save the C++ version of MyLibrary as a field inside the Java MyLibrary.
        MyLibrary * lib = new MyLibrary();
        env->SetLongField(javaMyLibrary, CPP_MYLIBRARY_POINTER_FIELD, ptr_to_jlong(lib));
    }
    
    JNIEXPORT void JNICALL Java_MyLibrary_setObserver(JNIEnv* env, 
            jobject javaMyLibrary, jobject javaObserver) {
        //Retrieve the CPP version of MyLibrary. For me, I stored it inside the java
        //object as a field, but you can implement it any way you want.
        MyLibrary* cppMyLibrary = (MyLibrary*)jlong_to_ptr(
            env->GetLongField(javaMyLibrary, CPP_MYLIBRARY_POINTER_FIELD));
        if (cppMyLibrary == NULL) {
            //Handle the problem
            return;
        }
        jthrowable exc = NULL;
    
        //Keep the jvm object around; the env is not available in another thread
        //but can be obtained from the jvm.
        JavaVM* jvm;
        env->GetJavaVM(&jvm);
    
        //The observer has to be made global; it's not available in another thread.
        jobject observer = env->NewGlobalRef(javaObserver);
        //TODO: retrieve the previous observer and clean it up with DeleteGlobalRef()
        //TODO: clean up this observer when destroying MyLibrary with DeleteGlobalRef()
    
        try {
            //At this point, both "jvm" and "observer" are accessible from the other thread.
            cppMyLibrary->setLogFunction([jvm, observer] (std::string& p0, std::string& p1) {
                JNIEnv* env;
                jvm->AttachCurrentThread(&env, NULL); //Obtain the env
                jclass clazz = env->GetObjectClass(observer);
                jmethodID meth = env->GetMethodID(clazz, "log",
                        "(Ljava/lang/String;Ljava/lang/String;)V");
                jstring s0 = env->NewStringUTF(p0.c_str());
                jstring s1 = env->NewStringUTF(p1.c_str());
                env->CallVoidMethod(observer, meth, s0, s1);
    
                //TODO: make sure this is called even if there's an exception!
                jvm->DetachCurrentThread();
            });
        } catch (...) {
            exc = //handle your exceptions here
        }
    
        if (exc != NULL) {
            env->Throw(exc);
        }
    }
    

    【讨论】:

    • 感谢您发布您的解决方案!自从您回答以来已经有很长一段时间了,您是否有机会实施 TODO 项目?如果是这样,您是否愿意用它来更新您的答案?
    • 对不起,我不再从事这个项目,也没有做那些 TODO。
    猜你喜欢
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    • 2022-01-01
    • 2017-05-04
    相关资源
    最近更新 更多