JNI를 사용해서 JAVA 코드에서 C/C++ 코드를 호출하는 것은 쉽습니다.
하지만 C/C++ 코드에서 JAVA 메소드를 호출하는 것은 상대적으로 복잡하더군요...

자바 코드는 다음과 같이 되어 있습니다.

* TestJNI.java

public class TestJNI {

       public native void setObj();

      

       public void printStr2(byte[] bytes) {

             Charset systemCharset = Charset.forName(System.getProperty("file.encoding"));

            

             String str = new String(bytes, systemCharset);

            

             System.out.println(str);

       }

      

       public void printStr() {

             System.out.println("c -> java");

       }

}


* jni_TestJNI.cpp

#include "jni_TestJNI.h"

#include "MyWU.h"

#include <iostream>

 

using namespace std;

 

JNIEXPORT void JNICALL Java_jni_TestJNI_setObj (JNIEnv* env, jobject obj) {

        MyWU* m = new MyWU(env, &obj);

        m->start();

}


* MyWU.cpp

#include "MyWU.h"

#include <iostream>

 

using namespace std;

 

MyWU::MyWU(JNIEnv* env, jobject* o) {

        this->env      = env;

        this->obj      = env->NewGlobalRef(*o);

 

        cout << "constructor" << endl;

}

 

MyWU::~MyWU(void) {

        this->env->DeleteGlobalRef(this->obj);

}

 

void MyWU::run() {

        cout << "run 1" << endl;

 

        activemq::concurrent::Thread::sleep(5000);

 

        JavaVM* jvm;

        env->GetJavaVM(&jvm);

        jvm->AttachCurrentThreadAsDaemon((void**)&env, NULL);

 

        jclass cls = env->GetObjectClass(obj);

        if ( cls == NULL ) return;

 

        cout << "run 2" << endl;

 

        jclass classString = (jclass) env->NewGlobalRef(cls);

        if ( classString == NULL ) return;

 

        cout << "run 3" << endl;

 

        jmethodID mid_getBytes = env->GetMethodID(classString, "printStr2", "([B)V");

 

        if (mid_getBytes == NULL) return;

 

        cout << "run 4" << endl;

 

        char* str = "한글테스트잘찍히나?";

 

        jbyteArray arg = env->NewByteArray(strlen(str));

        env->SetByteArrayRegion(arg, 0, strlen(str), (jbyte*)str);

 

        env->CallObjectMethod(obj, mid_getBytes, arg);

 

        //cout << "run 5" << endl;

}


this->obj      = env->NewGlobalRef(*o);

보시다 시피 먼저 인자로 받은 객체를 Global로 잡아놔야, GC에 영향을 안 받고 객체가 유지됩니다.

새로운 스레드에서 동작을 하고 있습니다. 만약 아무런 설정 없이 JAVA 메소드를 그대로 부르게 되면 Runtime 에러가 발생하게 됩니다. 그래서 다음과 같이

JavaVM* jvm;

env->GetJavaVM(&jvm);

jvm->AttachCurrentThreadAsDaemon((void**)&env, NULL);


JVM에 현재 스레드를 등록합니다.

그 다음에는 객체의 class와 메소드를 찾는 과정입니다. 이것은 뭐 JAVA의 리플렉션 기능이니...

몇줄 안되는 코드지만, 여기까지 알아내는데 꽤나 고생했다는...



Posted by アリア