http://www.javastudy.co.kr/docs/jhan/javaadvance/jnistring.html
이 섹션에서는 자바 프로그래밍 언어로 작성된 프로그램과 다른 언어로 작성된 프로그램 간에 문자열과 배열 데이터를 어떻게 전달하는지에 대하여 설명한다. (This section explains how to pass string and array data between a program written in the JavaTM programming language and other languages.)
- 문자열의 전달(Passing Strings)
- 배열의 전달(Passing Arrays)
- 배열 고정시키기(Pinning Array)
- 객체 배열(Object Arrays)
- 다차원 배열(Multi-Dimensional Arrays)
스트링의 전달(Passing Strings)
자바 언어의String
객체는 JNI에서 jstring
으로 표현되며, 16 비트 유니코드 문자열이다. C에서, 문자열은 기본적으로 8비트 문자들로부터 만들어진다. 그래서, C/C++ 함수에 전달된 자바 언어의 String
객체를 참조하거나, 또는 자바 언어 메소드로 리턴된 C/C++ 문자열을 참조하기 위해서는 원시 메소드 구현에 포함되어 있는 JNI 변환(conversion)함수들을 사용해야만 한다. (The String
object in the Java language, which is represented as jstring
in Java Native Interface (JNI), is a 16 bit unicode string. In C a string is by default constructed from 8 bit characters. So, to access a Java language String
object passed to a C or C++ function or return a C or C++ string to a Java language method, you need to use JNI conversion functions in your native method implementation.)GetStringUTFChar
함수는 UTF(Unicode Transformation Format)를 이용하여 16 비트 jstring
으로부터 8 비트 문자들을 얻어낸다. UTF는 유니코드를 어떤 정보의 손실도 없이8 비트 또는 16 비트 문자들로 표현한다. 세 번째 매개변수는 GetStringUTFChar
는 만약 jstring
의 지역 복사본을 만들었을 경우에는 JNI_TRUE
를 결과로 가지고, 그렇지 않을 경우에는 JNI_FALSE
를 그 결과로 한다. (The GetStringUTFChar
function retrieves 8-bit characters from a 16-bit jstring
using the Unicode Transformation Format (UTF). UTF represents Unicode as a string of 8 or 16 bit characters without losing any information. The third parameter GetStringUTFChar
results the result JNI_TRUE
if it made a local copy of the jstring
or JNI_FALSE
otherwise.)
C Version: (*env)->GetStringUTFChars(env, name, iscopy) C++ Version: env->GetStringUTFChars(name, iscopy)
다음에 나오는 C JNI 함수는 C 문자 배열을 jstring
으로 변환한다. (The following C JNI function converts an array of C characters to a jstring
:)
(*env)->NewStringUTF(env, lastfile)아래에 나오는 예제는
lastfile[80]
C 문자 배열을 jstring
으로 변환하여, 호출한 자바 언어 메소드에 리턴해 준다. (The example below converts the lastfile[80]
C character array to a jstring
, which is returned to the calling Java language method:)static char lastfile[80]; JNIEXPORT jstring JNICALL Java_ReadFile_lastFile (JNIEnv *env, jobject jobj) { return((*env)->NewStringUTF(env, lastfile)); }UTF 표현을 가지고 작업했다는 것이 끝났다는 것을 자바1 가상 머신이 알 수 있도록 하기 위해서는, 아래에 나타난 것처럼
ReleaseStringUTFChars
변환 함수를 호출하면 된다. 두 번째 매개변수는 UTF 표현을 구성하기 위해 사용된 원래의 jstring
값이고, 세 번째 매개변수는 그 String
의 지역 표현(local representation)에 대한 참조이다. (To let the Java1 virtual machine know you are finished with the UTF representation, call the ReleaseStringUTFChars
conversion function as shown below. The second argument is the original jstring
value used to construct the UTF representation, and the third argument is the reference to the local representation of that String
.)(*env)->ReleaseStringUTFChars(env, name, mfile);만약, 원시 코드가 중간적인(intermediate) UTF 표현 없이도 유니코드를 다루 수 있다면, 유니코드 문자열을 얻기 위해
GetStringChars
함수를 호출하고, ReleaseStringChars
함수를 호출하여 그 참조를 해제할 수 있다. (If your native code can work with Unicode, without needing the intermediate UTF representation, call theGetStringChars
function to retrieve the unicode string, and release the reference with a call to ReleaseStringChars
:)JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile (JNIEnv * env, jobject jobj, jstring name) { caddr_t m; jbyteArray jb; struct stat finfo; jboolean iscopy; const jchar *mfile = (*env)->GetStringChars(env, name, &iscopy); //... (*env)->ReleaseStringChars(env, name, mfile);
배열의 전달(Passing Arrays)
가장 마지막 섹션에서 보여주었던 예제에서,loadFile
원시 메소드는 바이트 배열로 파일의 내용을 리턴하고, 이는 자바 프로그래밍 언어에서는 기본형이다. 자바 언어에서 적당한 Type
Array 함수를 호출함으로써 기본형을 얻고(retrieve) 생성(create)할 수 있다. (In the example presented in the last section, the loadFile
native method returns the contents of a file in a byte array, which is a primitive type in the Java programming language. You can retrieve and create primitive types in the Java language by calling the appropriate Type
Array function.)예를 들어, 새로운 float 배열을 생성하기 위해서는, NewFloatArray
함수를 호출하거나, 또는 새로운 바이트 배열을 생성하기 위해서는 NewByteArray
함수를 호출하면 된다. 이름을 이런 방식으로 짓은 것은 배열에서 원소를 얻고, 원소를 추가하고, 배열 내의 원소를 변경하는 등에 대해서도 확장할 수 있다. 새로운 바이트 배열을 얻기 위해서는GetByteArrayElements
함수를 호출하면 된다. 배열에 원소를 추가하거나 또는 배열 내의 원소를 변경하기 위해서는 Set<type>ArrayElements
함수를 호출하면 된다. (For example, to create a new array of floats, call NewFloatArray
, or to create a new array of bytes, call NewByteArray
. This naming scheme extends to retrieving elements from, adding elements to, and changing elements in the array. To get a new array of bytes, call GetByteArrayElements
. To add elements to or change elements in the array, call Set<type>ArrayElements
.)
GetByteArrayElements
함수는 전체 배열에 영향을 미친다. 배열의 일부분에 대해 작업을 하기 위해서는 이 함수 대신 GetByteArrayRegion
함수를 사용할 수 있다. 그리고, 부분에 대해 배열의 원소를 변경하기 위한 Set<type>ArrayRegion
함수만 있다. 그러나, 부분(region)의 크기가 1일 수도 있고, 이는 존재하지 않는 Sete<type>ArrayElements
함수와 같다. (TheGetByteArrayElements
function affects the entire array. To work on a portion of the array, call GetByteArrayRegion
instead. There is only a Set<type>ArrayRegion
function for changing array elements. However the region could be of size 1, which is equivalent to the non-existent Sete<type>ArrayElements
.)
Native Code Type | Functions used |
---|---|
jboolean | NewBooleanArray |
GetBooleanArrayElements | |
GetBooleanArrayRegion/SetBooleanArrayRegion | |
ReleaseBooleanArrayRegion | |
jbyte | NewByteArray |
GetByteArrayElements | |
GetByteArrayRegion/SetByteArrayRegion | |
ReleaseByteArrayRegion | |
jchar | NewCharArray |
GetCharArrayElements | |
GetCharArrayRegion/SetCharArrayRegion | |
ReleaseCharArrayRegion | |
jdouble | NewDoubleArray |
GetDoubleArrayElements | |
GetDoubleArrayRegion/SetDoubleArrayRegion | |
ReleaseDoubleArrayRegion | |
jfloat | NewFloatArray |
GetFloatArrayElements | |
GetFloatArrayRegion/SetFloatArrayRegion | |
ReleaseFloatArrayRegion | |
jint | NewIntArray |
GetIntArrayElements | |
GetIntArrayRegion/SetIntArrayRegion | |
ReleaseIntArrayRegion | |
jlong | NewLongArray |
GetLongArrayElements | |
GetLongArrayRegion/SetLongArrayRegion | |
ReleaseLongArrayRegion | |
jobject | NewObjectArray |
GetObjectArrayElement/SetObjectArrayElement | |
jshort | NewShortArray |
GetShortArrayElements | |
GetShortArrayRegion/SetShortArrayRegion | |
ReleaseShortArrayRegion |
이전 섹션의 예제에 있는 loadFile
원시 메소드에서는, 배열의 부분을 읽고 있는 파일의 크기로 명세함으로써 전체 배열이 갱신된다. (In the loadFile
native method from the example in the previous section, the entire array is updated by specifying a region that is the size of the file being read in:)
jbyteArray jb; jb=(*env)->NewByteArray(env, finfo.st_size); (*env)->SetByteArrayRegion(env, jb, 0, finfo.st_size, (jbyte *)m); close(fd);배열은 호출한 자바 언어 메소드로 리턴되고, 계속해서 더 이상 사용되지 않을 때, 배열에 대한 참조를 쓰레기 수집된다. 이 배열은 다음과 같이 호출함으로써 명시적으로 할당 해제 될 수도 있다. (The array is returned to the calling Java language method, which in turn, garbage collects the reference to the array when it is no longer used. The array can be explicitly freed with the following call.)
(*env)-> ReleaseByteArrayElements(env, jb, (jbyte *)m, 0);위에서
ReleaseByteArrayElements
함수에 대한 마지막 매개변수는 다음과 같은 값을 가질 수 있다. (The last argument to the ReleaseByteArrayElements
function above can have the following values:)0
: C 코드 내로부터의 배열에 대한 갱신이 자바 언어 복사본에 반영된다. (Updates to the array from within the C code are reflected in the Java language copy.)JNI_COMMIT
: 자바 언어 복사본은 갱신되지만, 지역 변수jbyteArray
는 해제되지 않는다. (The Java language copy is updated, but the localjbyteArray
is not freed.)JNI_ABORT
: 변경이 다시 복사되지는 않지만,jbyteArray
는 해제된다. 만약 배열이 복사본임을 의미하는JNI_TRUE
얻기 모드(get mode)로 이 배열이 얻어진 경우에만 이 값이 사용될 수 있다. (Changes are not copied back, but thejbyteArray
is freed. The value is used only if the array is obtained with a get mode ofJNI_TRUE
meaning the array is a copy.)
배열 고정시키기(Pinning Array)
배열을 참조(retrieving)할 때, 이 배열이 복사본인지(JNI_TRUE
) 또는 자바 언어 프로그램 내에 존재하는 배열에 대한 참조인지(JNI_FALSE
)를 나타낼 수 있다. 만약, 배열에 대한 참조를 사용한다면, 배열이 자바 힙(Java heap) 내에 있고, 쓰레기 수집기가 힙 메모리를 최적으로 정리할 때 옮겨지지 않기를 원할 것이다. 배열 참조가 옮겨지지 않도록 하기 위해, 자바 가상 머신은 배열을 메모리 내에 고정시킨다(pinning; 못박는다). 배열을 고정시키는 것은 배열이 할당 해제될 때, 자바 가상 머신 내에서 정확한 요소들이 갱신되도록 하는 것을 보장한다. (When retrieving an array, you can specify if this is a copy (JNI_TRUE
) or a reference to the array residing in your Java language program (JNI_FALSE
). If you use a reference to the array, you will want the array to stay where it is in the Java heap and not get moved by the garbage collector when it compacts heap memory. To prevent the array references from being moved, the Java virtual machine pins the array into memory. Pinning the array ensures that when the array is released, the correct elements are updated in the Java VM.)이전 섹션의 loadfile
원시 메소드 예제에서는, 배열이 명시적으로 해제되지 않았다. 배열이 더 이상 필요없을 때, 배열이 쓰레기 수집되도록 하는 확실한 하나의 방법은 자바 언어 메소드를 호출하고, 대신 바이트 배열을 전달하고, 그리고 나서 지역 배열 복사본을 해제하는 것이다. 이러한 기법은 Multi-Dimensional Arrays에 대한 섹션에서 보여질 것이다. (In the loadfile
native method example from the previous section, the array is not explicitly released. One way to ensure the array is garbage collected when it is no longer needed is to call a Java language method, pass the byte array instead, and then free the local array copy. This technique is shown in the section on Multi-Dimensional Arrays.)
객체 배열(Object Arrays)
NewObjectArray
함수와 SetObjectArrayElement
함수를 호출함으로써 배열 내에 어떤 자바 언어 객체도 저장할 수 있다. 객체 배열과 기본형 배열의 주요 차이점은 jobjectarray
형을 생성할 때, 자바 언어 클래스가 매개변수로서 사용된다는 것이다. (You can store any Java language object in an array with the NewObjectArray
andSetObjectArrayElement
function calls. The main difference between an object array and an array of primitive types is that when constructing a jobjectarray
type, the Java language class is used as a parameter.)다음에 나오는 C++ 예제는 String
객체 배열을 생성하기 위해 NewObjectArray
함수를 어떻게 호출하는지를 보여주고 있다. 배열의 크기는 5(five)로 설정되고, FindClass
함수 호출에서는 클래스 정의 리턴되고, 배열의 요소들은 공백 문자열로 초기화 된다. 배열 내에 저장할 위치(position)와 값을 가지고 SetObjectArrayElement
함수를 호출함으로써 배열의 요소들을 갱신할 수 있다. (This next C++ example shows how to call NewObjectArray
to create an array of String
objects. The size of the array is set to five, the class definition is returned from a call to FindClass
, and the elements of the array are initialized with an empty string. The elements of the array are updated by calling SetObjectArrayElement
with the position and value to put in the array.)
#include <jni.h> #include "ArrayHandler.h" JNIEXPORT jobjectArray JNICALL Java_ArrayHandler_returnArray (JNIEnv *env, jobject jobj){ jobjectArray ret; int i; char *message[5]= {"first", "second", "third", "fourth", "fifth"}; ret= (jobjectArray)env->NewObjectArray(5, env->FindClass("java/lang/String"), env->NewStringUTF("")); for(i=0;i<5;i++) { env->SetObjectArrayElement( ret,i,env->NewStringUTF(message[i])); } return(ret); }이 원시 메소드를 호출하는 자바 클래스는 다음과 같다. (The Java class that calls this native method is as follows:)
public class ArrayHandler { public native String[] returnArray(); static{ System.loadLibrary("nativelib"); } public static void main(String args[]) { String ar[]; ArrayHandler ah= new ArrayHandler(); ar = ah.returnArray(); for (int i=0; i<5; i++) { System.out.println("array element"+i+ "=" + ar[i]); } } }
다차원 배열(Multi-Dimensional Arrays)
자바 언어 프로그램에서 원시 메소드를 이용하여 기존의 알제브라 라이브러리 CLAPACK/LAPACK과 같은 수학적 라이브러리 또는 행렬 연산 프로그램 등을 호출할 필요가 있을 것이다. 이러한 라이브러리와 프로그램들 대부분은 2차원 또는 다차원 배열을 사용한다. (You might need to call existing numerical and mathematical libraries such as the linear algebra library CLAPACK/LAPACK or other matrix crunching programs from your Java language program using native methods. Many of these libraries and programs use two-dimensional and higher order arrays.)자바 프로그래밍 언어에서, 1차원 이상의 배열은 배열에 대한 배열로 취급된다. 예를 들어, 2차원 정수 배열(two-dimensional integer array)은 정수 배열에 대한 배열로 다루어진다. 배열은 수평적으로 읽히고, 이러한 방식을 행 우선(row order)이라 한다. (In the Java programming language, any array that has more than one dimension is treated as an array of arrays. For example, a two-dimensional integer array is handled as an array of integer arrays. The array is read horizontally, or what is also termed as row order.)
FORTRAN과 같은 다른 언어에서는 열 우선(column ordering) 방식을 사용하기 때문에, 만약 프로그램이 언어 배열을 FORTRAN 함수에 건네주게 될 때는 추가적인 주의가 필요하다. 또한, 자바 프로그래밍 언어로 작성된 애플리케이션 내에서 배열 요소들이 메모리 상에서 연속적일 것이라고 보장할 수 없다. 몇몇 수학 라이브러리들은 속도 최적화(speed optimizations)를 수행하기 위해 배열 요소들이 메모리 내에서 연속적으로 저장되어 있다고 생각하기 때문에, 그러한 함수에 배열을 전달하기 위해서는 추가적인 지역 복사본을 만들어 주어야 한다. (Other languages such as FORTRAN use column ordering so extra care is needed if your program hands a Java language array to a FORTRAN function. Also, the array elements in an application written in the Java programming language are not guaranteed to be contiguous in memory. Some numerical libraries use the knowledge that the array elements are stored next to each other in memory to perform speed optimizations, so you might need to make an additional local copy of the array to pass to those functions.)
다음에 나오는 예제는 요소들을 추려낸 후, 연산을 수행하고, 그리고 그 결과를 되돌려 주기 위해 자바 언어를 호출하는 원시 메소드에게 2차원 배열을 전달하고 있다. (The next example passes a two-dimensional array to a native method which then extracts the elements, performs a calculation, and calls a Java language method to return the results.)
이 배열은 jints
배열을 포함하고 있는 객체 배열로서 전달된다. GetObjectArrayElement
함수를 호출함으로써 객체 배열로부터 jintArray
인스턴스를 먼저 가져오고, 그리고 나서jintArray
행으로부터 요소들을 추출함으로써 각 요소들이 추출된다. (The array is passed as an object array that contains an array of jints
. The individual elements are extracted by first retrieving a jintArray
instance from the object array by calling GetObjectArrayElement
, and then extracting the elements from the jintArray
row.)
이 예제는 고정 크기의 행렬을 사용한다. 만약, 사용되고 있는 배열의 크기를 모를 경우에는, 가장 바깥쪽 배열의 크기를 되돌려 주는 GetArrayLength(array)
함수를 사용하면 된다. 배열의 전체 크기를 알기 위해서는 배열의 각 차원에 대해 GetArrayLength(array)
함수를 호출하면 된다. (The example uses a fixed size matrix. If you do not know the size of the array being used, the GetArrayLength(array)
function returns the size of the outermost array. You will need to call the GetArrayLength(array)
function on each dimension of the array to discover the total size of the array.)
자바 언어로 작성된 프로그램으로 되돌려진 새로운 배열은 역으로 생성된다. 먼저, jintArray
인스턴스가 생성되고, 이 인스턴스는 SetObjectArrayElement
함수를 호출함으로써 객체 배열 내에 설정된다. (The new array sent back to the program written in the Java language is built in reverse. First, a
jintArray
instance is created and that instance is set in the object array by calling SetObjectArrayElement
.)
public class ArrayManipulation { private int arrayResults[][]; Boolean lock=new Boolean(true); int arraySize=-1; public native void manipulateArray( int[][] multiplier, Boolean lock); static{ System.loadLibrary("nativelib"); } public void sendArrayResults(int results[][]) { arraySize=results.length; arrayResults=new int[results.length][]; System.arraycopy(results,0,arrayResults, 0,arraySize); } public void displayArray() { for (int i=0; i<arraySize; i++) { for(int j=0; j <arrayResults[i].length;j++) { System.out.println("array element "+i+","+j+ "= " + arrayResults[i][j]); } } } public static void main(String args[]) { int[][] ar = new int[3][3]; int count=3; for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { ar[i][j]=count; } count++; } ArrayManipulation am= new ArrayManipulation(); am.manipulateArray(ar, am.lock); am.displayArray(); } } #include <jni.h> #include <iostream.h> #include "ArrayManipulation.h" JNIEXPORT void JNICALL Java_ArrayManipulation_manipulateArray (JNIEnv *env, jobject jobj, jobjectArray elements, jobject lock){ jobjectArray ret; int i,j; jint arraysize; int asize; jclass cls; jmethodID mid; jfieldID fid; long localArrayCopy[3][3]; long localMatrix[3]={4,4,4}; for(i=0; i<3; i++) { jintArray oneDim= (jintArray)env->GetObjectArrayElement( elements, i); jint *element=env->GetIntArrayElements(oneDim, 0); for(j=0; j<3; j++) { localArrayCopy[i][j]= element[j]; } } // With the C++ copy of the array, // process the array with LAPACK, BLAS, etc. for (i=0;i<3;i++) { for (j=0; j<3 ; j++) { localArrayCopy[i][j]= localArrayCopy[i][j]*localMatrix[i]; } } // Create array to send back jintArray row= (jintArray)env->NewIntArray(3); ret=(jobjectArray)env->NewObjectArray( 3, env->GetObjectClass(row), 0); for(i=0;i<3;i++) { row= (jintArray)env->NewIntArray(3); env->SetIntArrayRegion((jintArray)row,( jsize)0,3,(jint *)localArrayCopy[i]); env->SetObjectArrayElement(ret,i,row); } cls=env->GetObjectClass(jobj); mid=env->GetMethodID(cls, "sendArrayResults", "([[I)V"); if (mid == 0) { cout <<"Can't find method sendArrayResults"; return; } env->ExceptionClear(); env->MonitorEnter(lock); env->CallVoidMethod(jobj, mid, ret); env->MonitorExit(lock); if(env->ExceptionOccurred()) { cout << "error occured copying array back" << endl; env->ExceptionDescribe(); env->ExceptionClear(); } fid=env->GetFieldID(cls, "arraySize", "I"); if (fid == 0) { cout <<"Can't find field arraySize"; return; } asize=env->GetIntField(jobj,fid); if(!env->ExceptionOccurred()) { cout<< "Java array size=" << asize << endl; } else { env->ExceptionClear(); } return; }
'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 (Java Native Interface) - Parameter (0) | 2011.05.19 |
Android NDK Windows setup (0) | 2011.05.18 |