2012년 1월 6일 금요일

[JNI] 안드로이드 JNI 환경에서 C++과 Java 간의 한글 데이터 전송 문제

출처 : http://snowbora.com/371

지금은 잠깐 쉬고 있지만, 예전에 안드로이드 ndk 환경에서 프로그래밍을 하던 때가 있었습니다.
그 때, JNI 인터페이스를 통해 Native단의 C++과 Application단의 Java와의 데이터를 주고 받아야 하는 API들을 
만들었었는데, 그 중 문자열 전송과 관련해서 한글이 죄다 깨져서 전송되는 현상이 있었습니다.

Java 단에서는 UTF-8 방식을 이용하고, C++에서는 KSC5601 방식의 문자열이었기 때문에 발생하는 문제였습니다.
그래서 구글링을 통해 C++과 Java간의 한글 변환 관련 코드를 확보 및 컨버팅을 했습니다.


C++과 Java간의 한글 변환 코드는 다음과 같습니다. 다음은 헤더 파일입니다.
01.#ifndef _STRING_CONVERTER_FOR_JNI_
02.#define _STRING_CONVERTER_FOR_JNI_
03. 
04.#include <jni.h>
05.#include <stdio.h>
06.#include <string.h>
07. 
08.#ifdef __cplusplus
09.extern "C"
10.{
11.#endif
12. 
13.char *jbyteArray2cstr( JNIEnv *env, jbyteArray javaBytes );
14.jbyteArray cstr2jbyteArray( JNIEnv *env, const char *nativeStr);
15.jbyteArray javaGetBytes( JNIEnv *env, jstring str );
16.jbyteArray javaGetBytesEncoding( JNIEnv *env, jstring str, const char *encoding );
17.jstring javaNewString( JNIEnv *env, jbyteArray javaBytes );
18.jstring javaNewStringEncoding(JNIEnv *env, jbyteArray javaBytes, const char *encoding );
19. 
20.jstring javaNewStringChar(JNIEnv* env, const char* nativeStr);
21. 
22.#ifdef __cplusplus
23.}
24.#endif
25. 
26.#endif //_STRING_CONVERTER_FOR_JNI_
27.</string.h></stdio.h></jni.h>

그리고 다음은 C++ 소스 코드입니다.
001.#include "StringConverter_For_JNI.h"
002. 
003.static jclass class_String;
004.static jmethodID mid_getBytes, mid_getBytesEncoding;
005.static jmethodID mid_newString, mid_newStringEncoding;
006. 
007.char *jbyteArray2cstr( JNIEnv *env, jbyteArray javaBytes )
008.{
009.size_t len = env->GetArrayLength(javaBytes);
010.jbyte *nativeBytes = env->GetByteArrayElements(javaBytes, 0);
011.char *nativeStr = (char *)malloc(len+1);
012.strncpy( nativeStr, (const char*)nativeBytes, len );
013.nativeStr[len] = '\0';
014.env->ReleaseByteArrayElements(javaBytes, nativeBytes, JNI_ABORT);
015. 
016.return nativeStr;
017.}
018. 
019.jbyteArray cstr2jbyteArray( JNIEnv *env, const char *nativeStr)
020.{
021.jbyteArray javaBytes;
022.int len = strlen( nativeStr );
023.javaBytes = env->NewByteArray(len);
024.env->SetByteArrayRegion(javaBytes, 0, len, (jbyte *) nativeStr );
025. 
026.return javaBytes;
027.}
028. 
029.jbyteArray javaGetBytes( JNIEnv *env, jstring str )
030.{
031.if ( mid_getBytes == 0 )
032.{
033.if ( class_String == 0 )
034.{
035.jclass cls = env->FindClass("java/lang/String");
036.if ( cls == 0 )
037.{
038.return 0;
039.}
040. 
041.class_String = (jclass)env->NewGlobalRef(cls);
042.env->DeleteLocalRef(cls);
043.if ( class_String == 0 )
044.{
045.return 0;
046.}
047.}
048. 
049.mid_getBytes = env->GetMethodID(class_String, "getBytes""()[B");
050.if (mid_getBytes == 0)
051.{
052.return 0;
053.}
054.}
055. 
056.return (jbyteArray)env->CallObjectMethod(str, mid_getBytes );
057.}
058. 
059.jbyteArray javaGetBytesEncoding( JNIEnv *env, jstring str, const char *encoding )
060.{
061.if ( mid_getBytesEncoding == 0 )
062.{
063.if ( class_String == 0 )
064.{
065.jclass cls = env->FindClass("java/lang/String");
066.if ( cls == 0 )
067.{
068.return 0;
069.}
070. 
071.class_String = (jclass)env->NewGlobalRef(cls);
072.env->DeleteLocalRef(cls);
073.if ( class_String == 0 )
074.{
075.return 0;
076.}
077.}
078. 
079.mid_getBytesEncoding = env->GetMethodID(class_String, "getBytes""(Ljava/lang/String;)[B");
080.if (mid_getBytesEncoding == 0)
081.{
082.return 0;
083.}
084.}
085. 
086.jstring jstr = env->NewStringUTF(encoding);
087.jbyteArray retArray = (jbyteArray)env->CallObjectMethod(str, mid_getBytesEncoding, jstr);
088.env->DeleteLocalRef(jstr);
089. 
090.return retArray;
091.}
092. 
093.jstring javaNewString( JNIEnv *env, jbyteArray javaBytes )
094.{
095.if ( mid_newString == 0 )
096.{
097.if ( class_String == 0 )
098.{
099.jclass cls = env->FindClass("java/lang/String");
100.if ( cls == 0 )
101.{
102.return 0;
103.}
104. 
105.class_String = (jclass)env->NewGlobalRef(cls);
106.env->DeleteLocalRef(cls);
107.if ( class_String == 0 )
108.{
109.return 0;
110.}
111.}
112. 
113.mid_newString = env->GetMethodID(class_String, "<init>""([B)V");
114.if ( mid_newString == 0 )
115.{
116.return 0;
117.}
118.}
119. 
120.return (jstring)env->NewObject(class_String, mid_newString, javaBytes );
121.}
122. 
123.jstring javaNewStringEncoding(JNIEnv *env, jbyteArray javaBytes, const char *encoding )
124.{
125.int len;
126.jstring str;
127.if ( mid_newString == 0 )
128.{
129.if ( class_String == 0 )
130.{
131.jclass cls = env->FindClass("java/lang/String");
132.if ( cls == 0 )
133.{
134.return 0;
135.}
136. 
137.class_String = (jclass)env->NewGlobalRef(cls);
138.env->DeleteLocalRef(cls);
139.if ( class_String == 0 )
140.{
141.return 0;
142.}
143. 
144.}
145. 
146.mid_newString = env->GetMethodID(class_String, "<init>""([BLjava/lang/String;)V");
147.if ( mid_newString == 0 )
148.{
149.return 0;
150.}
151.}
152. 
153.jstring jstr = env->NewStringUTF(encoding);
154.str = (jstring)env->NewObject(class_String, mid_newString, javaBytes, jstr);
155.env->DeleteLocalRef(jstr);
156. 
157.return str;
158.}
159. 
160.jstring javaNewStringChar(JNIEnv* env, const char* nativeStr)
161.{
162.jbyteArray byteArray = cstr2jbyteArray(env, nativeStr);
163.jstring jstr = javaNewString(env, byteArray);
164.env->DeleteLocalRef(byteArray);
165. 
166.return jstr;
167.}
168.</init></init>


이 함수들을 이용해서 실제로 사용한 예제는 다음과 같습니다. 
C++의 string을 Java용 jstring으로 변환하는 부분
1.jstring jstrValue;
2.jfieldID fieldID;
3.jmethodID methodID;
4. 
5.methodID = env->GetMethodID( cProgramInfo, "ProgramName","(Ljava/lang/String;)V");
6.jstrValue = javaNewStringChar(env, strProgramName);
7.env->CallVoidMethod( jElement, methodID, jstrValue);
8.env->DeleteLocalRef(jstrValue);

 Java용 jstring를 C++의 string으로 변환하는 함수
01.string      title_;
02.jmethodID methodID;
03. 
04.methodID = env->GetMethodID( cProgramInfo, "ProgramTitle","()Ljava/lang/String;");
05.jstring jstrTitle = (jstring)env->CallObjectMethod(jElement, methodID);
06.if(jstrTitle)
07.{
08.pszTitle = (char*)env->GetStringUTFChars(jstrTitle, &iscopy);
09.title_ = pszTitle;
10.env->ReleaseStringUTFChars(jstrTitle, pszTitle);
11.env->DeleteLocalRef(jstrTitle); 
12.}

댓글 없음:

댓글 쓰기