JNI (Java Native Interface) - Parameter

|
Simple example을 통해서 JNI를 이용하여 C/C++함수를 호출하는 방법에 대해 간략히 정리 하였다.
이제 Java module과 C/C++ module사이에 parameter를 통하여 정보를 주고 받는 방법을 알아보자

우선 기본적으로 Java와 C/C++사이의 type을 매핑 시키기 위해 jni.h에 보면 다양한 Java의 변수 type에 대해 C/C++에서 사용할 수 있도록 정의해두었다.
자세한건 다른 잘 정리된 사이트를 참고하고 여기서는 내가 사용한 String type을 넘기고 받는 부분에 대해 알아 보자

Java Source
public class HelloWorld {
    static {
        System.loadLibrary("HelloWorld");
    }

    String test1;

    public native void printHelloWorld(String name);

    public static void main(String[] args) {
        HelloWorld hw = new HelloWorld();
        hw.printHelloWorld("C/C++ module");
        System.out.println("Java : " + hw.test1);
    }
}

Java Compile
javac HelloWorld.java
javah -jni HelloWorld

C/C++ Source
JNIEXPORT void JNICALL Java_HelloWorld_printHelloWorld(JNIEnv *env, jobject obj, jstring name)
{
    jclass cls;
    jfieldID jFieldId;
    jstring test1;
    jboolean iscopy;
#ifdef __cplusplus
    const char *sentence = (env)->GetStringUTFChars(name, &iscopy);

    printf("C/C++ : %s\n", sentence);

    (env)->ReleaseStringUTFChars(name, sentence);

    cls = (env)->GetObjectClass(obj);
    jFieldId = (env)->GetFieldID(cls, "test1", "Ljava/lang/String;");
    if(jFieldId == 0) {
        printf("test1 - Field strVal not Found in Hello class\n");
        return;
    }
    test1 = (env)->NewStringUTF("Java module");
    (env)->SetObjectField(obj, jFieldId, test1);
#else
    const char *sentence = (*env)->GetStringUTFChars(env, name, &iscopy);

    printf("C/C++ : %s\n", sentence);

    (*env)->ReleaseStringUTFChars(env, name, sentence);

    cls = (*env)->GetObjectClass(env, obj);
    jFieldId = (*env)->GetFieldID(env, cls, "test1", "Ljava/lang/String;");
    if(jFieldId == 0) {
        printf("test1 - Field strVal not Found in Hello class\n");
        return;
    }
    test1 = (*env)->NewStringUTF(env, "Java module");
    (*env)->SetObjectField(env, obj, jFieldId, test1);
#endif

    return;
}

C/C++ Compile 및 실행은 Simple example과 동일

참고 사항
1.
public native void printHelloWorld(String name) : Java module에서 정의한 method
JNIEXPORT void JNICALL Java_HelloWorld_printHelloWorld(JNIEnv *env, jobject obj, jstring name) 위 java method에 해당하는 C module

양쪽 parameter의 type을 맞추기 위해 jni.h에 보면 Java type에 해당하는 C type이 정의되어 있다.
예를들어 위 예제에서와 같이 Java의 String type은 C의 jstring type으로 정의 되어 있다.

2. 
jstring과 같은 type은 C에서 그대로 사용할 수 없고 C에사 사용가능한 char type으로 변환 시켜줘야한다.
각 object나 array type에 맞게 이러한 역할을 하는 함수가 있다.
String같은 경우, const char *sentence = (env)->GetStringUTFChars(name, &iscopy) 이처럼 char pointer type의 sentence로 저장한다.

3. 
C module에서 Java module로 값을 넘겨줄때, return type을 이용할 수도 있고, Java class의 variable을 직접 access할 수 도 있다. 위 예제는 java class의 variable에 직접 값을 넣는 방법을 알아봤다.
cls = (env)->GetObjectClass(obj);
jFieldId = (env)->GetFieldID(cls, "test1", "Ljava/lang/String;");
java class정보를 얻기위해 GetObjectClass를 이용하였다. 이후, GetFieldID를 이용하여 class안의 field에 대한 정보를 구한다. 예제에서는 test1 은 variable name, Ljava/lang/String은 variable type을 나타낸다. 각각의 type 에 따라 지정된 값이 있다. 예를들면 I : int, F : float 등등 (다른 reference 참고)

4. 
 test1 = (env)->NewStringUTF("Java module");
(env)->SetObjectField(obj, jFieldId, test1);
UTF encoding을 사용하는 String객체를 생성한다. 여기서 test1은 당연히 jstring type 이어야 한다.
그후 jField정보를 이용하여 test1 (jstring type)을 class의 특정 field에 넣는다.

5.
C와 C++에 따라서 함수 호출 방법이 조금다르다. 
예를 들어 C++에서는 (env)->NewStringUTF("Java module"); 와 같이 호출하는 반면, C에서는 (*env)->NewStringUTF(env, "Java module"); 와 같이 호출한다. 이건 jni.h에 가서 JNIEnv 정의된걸 보면 알 수 있다.

'Android 개발 > Android NDK / JNI' 카테고리의 다른 글

안드로이드 JNI 환경에서 C++과 Java 간의 한글 데이터 전송 문제  (0) 2011.05.30
jni.h 소스 파일  (0) 2011.05.30
jni.h File Reference  (0) 2011.05.30
JNI 기술  (0) 2011.05.19
Android NDK Windows setup  (0) 2011.05.18
And