Hello JNI (3), C 라이브러리에 문자열 전달

From Evernote:

Hello JNI, C 라이브러리에 문자열 전달

이전 글에서 작성한 Hello Jni 프로젝트를 다시 한 번 더 수정해서, 이번에는 C 라이브러리 쪽으로 문자열을 전달하고, C에서는 전달받은 문자열을 화면에 출력하도록 작성한다.


다음과 같이 자바 소스 파일에 네이티브 메서드를 하나 추가한다. 


package kr.pe.elex.example.jni;

/**
 * JNI 테스트
 *
 * @version 3
 * @author Elex
 */
public class HelloJni {

       static {
            System. loadLibrary("libhellojni");
      }

       /**
       * C 언어에서 한 줄 출력한다.
       */
       private native void printOut();

       /**
       * C 언어에서 문자열을 전달 받는다.
       *
       * @return
       */
       private native String getStringFromC();

       /**
       * C 언어에 문자열을 전달하고, C에서는 전달받은 문자열을 출력한다.
       *
       * @param str
       */
       private native void putStringToC(String str);

       public static void main(String[] args) {

            HelloJni test = new HelloJni();
             // test.printOut();
             //System.out.println(test.getStringFromC());
            test.putStringToC( "Hello Jni to C");
      }

}

#. putStringToC()라는 네이티브 메서드를 추가했다. 이 메서드는 매개변수로 String을 전달 받는다.

#. 그리고, 새로 추가한 메서드가 실행되도록 메인 메서드를 조금 수정했다.

#. 새로 생성된 자바 클래스 파일로부터 javah 도구를 사용해서 헤더 파일을 추출하면 아래와 같다. (함수명 앞에 밑줄문자를 추가해 준다.)



    /*
     * Class:     kr_pe_elex_example_jni_HelloJni
     * Method:    putStringToC
     * Signature: ( Ljava/lang /String;)V
     */
    JNIEXPORT void JNICALL
    _Java_kr_pe_elex_example_jni_HelloJni_putStringToC(JNIEnv *, jobjectjstring);

#. 자바로부터 전달은 매개변수가 세 번째 인자로 추가된 것을 확인할 수 있다. jstring 타입이다.




헤더 파일에 정의된 함수 원형을 기초로 C 소스 파일에 새로운 함수를 구현하면, 아래와 같다. 이전 프로젝트에서 맨 마지막 함수만 새로 추가되었다.

/*
 * hellojni.c
 * JNI 테스트를 위한 C 라이브러리
 *      Author: Elex
 */

#include "kr_pe_elex_example_jni_HelloJni.h"

JNIEXPORT void JNICALL
_Java_kr_pe_elex_example_jni_HelloJni_printOut (JNIEnv *env, jobject obj)
{
  puts( "Hello Jni !");
}

JNIEXPORT jstring JNICALL
_Java_kr_pe_elex_example_jni_HelloJni_getStringFromC( JNIEnv *env, jobject obj)
{
  return (*env)-> NewStringUTF(env, "Hello Jni from C!");
}

JNIEXPORT void JNICALL
_Java_kr_pe_elex_example_jni_HelloJni_putStringToC (JNIEnv *env, jobject obj,
    jstring str)
{
  const char* s;
  s = (*env)->GetStringUTFChars(env, str, NULL);
  if (s != NULL)
    {
      printf( "%s", s);
    }
  (*env)->ReleaseStringUTFChars(env, str, s);
}

#. jstring 타입은 C언어의 문자열과 다르므로, printf(%s, str)이라고 사용할 수는 없다. jstring 타입을 C 언어의 문자열 표현으로 변환해야 한다.

#. JNIEnv 인터페이스 포인터를 통해서 GetStringUTFChars() 함수를 사용하면 jstring 형식으로부터 UTF8 형식의 문자열로 변환할 수 있다.

#. GetStringUTFChars() 함수의 세 번째 매개변수 jboolean *isCopy를 통해 jstring 문자열이 java.lang.String 인스턴스의 사본인지 아닌지를 전달 받는다. JNI_TRUE 또는 JNI_FALSE이다. 만일, JNI_FALSE인 때에는 네이티브 코드에서 해당 문자열을 수정해서는 안된다. (대부분의 경우 확인할 필요가 없으니 NULL을 주면 된다고 한다.)

#. 간혹 메모리 할당이 실패할 경우도 있으므로 GetStringUTFChars()의 리턴 값이 NULL인지 확인 해야한다. NULL인 때에는 OutOfMemoryError가 던져질 것이다.

#. GetStringUTFChars()로 가져온 문자열의 사용이 끝났다면 ReleaseStringUTFChars()를 사용해서 메모리를 놓아주어야 한다. 그렇지 않으면 메모리 누수가 발생할지도 모른다. ReleaseStringUTFChars()는 자바의 가비지 콜렉터에게 수집되어도 괜찮다는 의사표시라고 볼 수 있다.

#. NewStringUTF(), GetStringUTFChars(), ReleaseStringUTFChars()와 유사한 NewString(), GetStringChars(), ReleaseStringChars() 함수들도 있는데, 전자들은 UTF-8 인코딩으로 문자열로 처리하는 반면, 후자들은 유니코드 인코딩으로 문자열을 처리한다. 후자의 경우 문자열 끝에 널문자가 포함되지 않는다.

#. 또한, 문자열의 길이를 확인하기 위해 GetStringLength(), GetStringUTFLength() 함수를 사용할 수도 있다.



<프로젝트 파일>
http://www.elex.pe.kr/attachment/cfile23.uf@2204424F522F4591353E3C.7z


댓글

이 블로그의 인기 게시물

자바 암호화 확장 (JCE) 관련 자바 1.8.0_151 이후 변경 사항

좌표 변환: 회전 이동

Apache Commons CSV