하지만 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;
}
보시다 시피 먼저 인자로 받은 객체를 Global로 잡아놔야, GC에 영향을 안 받고 객체가 유지됩니다.
새로운 스레드에서 동작을 하고 있습니다. 만약 아무런 설정 없이 JAVA 메소드를 그대로 부르게 되면 Runtime 에러가 발생하게 됩니다. 그래서 다음과 같이
env->GetJavaVM(&jvm);
jvm->AttachCurrentThreadAsDaemon((void**)&env, NULL);
JVM에 현재 스레드를 등록합니다.
그 다음에는 객체의 class와 메소드를 찾는 과정입니다. 이것은 뭐 JAVA의 리플렉션 기능이니...
몇줄 안되는 코드지만, 여기까지 알아내는데 꽤나 고생했다는...