2013-11-28 2 views
3

최근에는 자바 얼굴로 C/C++ 라이브러리를 래핑해야했습니다. 메서드 중 하나는 함수를 매개 변수로 받아들입니다. 당신이 함수를 통과 할 수 있지만 로그() 메소드를 사용하여 개체를 전달할 수있는 자바 측면에서JNI에서 Observer 패턴을 구현하는 방법

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

: 이것은 기본적으로 옵저버 (일명 리스너) 패턴이다.

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()를 구현합니까?

답변

6

이 솔루션을 구현하는 데 영원히 걸렸습니다. 나는 인터넷에서 정보를 끌어 와야했다. 여기에 있습니다 :

//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); 
    } 
} 
관련 문제