프로그래밍/Java

JNI(Java Native Interface) 이용 방법 (3/6) - 3.3 ArrayReturn

산을좋아한라쯔 2015. 7. 24. 20:23
반응형

3.3 배열값을 반환(배열값: Java<-C)

이번 챕터는 배열을 주고 받는 예이다. 3.3에서는 C코드로부터 배열값이 리턴되는 경우, 3.4에서는 자바의 배열값이 C로 전달되는 예를 볼 것이다.

 

JNI에서의 배열처리는 앞의 3.1과 3.2에서의 기본변수값의 처리와 달리 좀 더 복잡하다. 기본 변수의 경우는 그 변수에다가 직접 값을 부여하고 전달하면 되었는데, 배열에 대해서는 직접 입력이 안되고 별도 함수를 이용해야 한다.

다음 코드는 byte array를 리턴하는 C코드이다.

 

JNIEXPORT jbyteArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedCharArr(JNIEnv *env, jobject obj){

         jbyteArray retArr = (*env)->NewByteArray(env, 5);

         char tmpArr[5] = { 1, 2, 3, 4, 5 };

 

         (*env)->SetByteArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

위 코드를 보면 NewByteArray와 SetByteArrayRegion 이라는 두 개의 함수가 사용되었다. 다른 변수타입 배열에도 동일하게 적용될 패턴이다.

  1) NewByteArray를 이용해서 jbyteArray를 만들고,

  2) jbyteArray에 값을 넣기위한 기본형의 임시 배열변수를 만들어서 값을 지정하고,

  3) SetByteArrayRegion을 이용해서, 기본형 배열변수의 값을 jbyteArray로 붙여넣는다.

 

JNI에서 배열변수를 생성하는 New<Primitive>Array()형태의 함수와 배열에 값을 설정하는 Set<Primitive>ArrayRegion()함수는 각각 8종이 존재한다.

 

New<PrimitiveType>Array Routines Array Type
NewBooleanArray() jbooleanArray
NewByteArray() jbyteArray
NewCharArray() jcharArray
NewShortArray() jshortArray
NewIntArray() jintArray
NewLongArray() jlongArray
NewFloatArray() jfloatArray
NewDoubleArray() jdoubleArray

 

 

Set<PrimitiveType>ArrayRegion Routine Array Type Native Type
SetBooleanArrayRegion() jbooleanArray jboolean
SetByteArrayRegion() jbyteArray jbyte
SetCharArrayRegion() jcharArray jchar
SetShortArrayRegion() jshortArray jshort
SetIntArrayRegion() jintArray jint
SetLongArrayRegion() jlongArray jlong
SetFloatArrayRegion() jfloatArray jfloat
SetDoubleArrayRegion() jdoubleArray jdouble

 

 

jbooleanArray (JNICALL *NewBooleanArray)   (JNIEnv *env, jsize len);

jbyteArray (JNICALL *NewByteArray)   (JNIEnv *env, jsize len);

jcharArray (JNICALL *NewCharArray)   (JNIEnv *env, jsize len);

jshortArray (JNICALL *NewShortArray)    (JNIEnv *env, jsize len);

jintArray (JNICALL *NewIntArray)   (JNIEnv *env, jsize len);

jlongArray (JNICALL *NewLongArray)   (JNIEnv *env, jsize len);

jfloatArray (JNICALL *NewFloatArray)  (JNIEnv *env, jsize len);

jdoubleArray (JNICALL *NewDoubleArray)  (JNIEnv *env, jsize len);

 

void (JNICALL *SetBooleanArrayRegion)

      (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf);

void (JNICALL *SetByteArrayRegion)

      (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf);

void (JNICALL *SetCharArrayRegion)

      (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf);

void (JNICALL *SetShortArrayRegion)

      (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf);

void (JNICALL *SetIntArrayRegion)

      (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf);

void (JNICALL *SetLongArrayRegion)

      (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf);

void (JNICALL *SetFloatArrayRegion)

      (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf);

void (JNICALL *SetDoubleArrayRegion)

      (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf);

 

 

C의 각 배열변수에 대한 자바 변수 매핑은 앞 예제에서 살펴본 '기본 변수'에 대한 매핑과 동일하다. 표로 나타내면 다음과 같다.

 

 

번호

Storage size

C 언어

JNI Type

java type

 1

1

char[]

jbyteArray

byte[]

 2

1

unsigned char[]

jcharArray

char[]

 3

2

short[]

jshortArray

short[]

 4

2

unsigned short[]

jcharArray

int[]

 5

4(2)

int[]

jintArray

int[]

 6

4(2)

unsigned int[]

jlongArray

long[]

 7

4

long[]

jintArray

int[]

 8

4

unsigned long[]

jlongArray

long[]

 9

4

float[]

jfloatArray

float[]

 10

8

double[]

jdoubleArray

double[]

 11

8

long long[]

jlongArray

long[]

 

'기본 변수'에 대한 처리와 달리, 배열값 전달에서는 위 표의 빨간색 배열변수를 처리할 때 주의를 해야한다. C언어에서의 변수에 할당된 크기와 Java에서의 변수크기가 다르기 때문에 고려해줘야할 사항이 있다.

위 표에서 2번째 행에 있는 unsigned char형 배열을 다룰 때의 코드를 보면 살펴보자. 아래는 unsigend char형 배열값을 jcharArray로 리턴하는 C코드이다.

 

JNIEXPORT jcharArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedUnsignedCharArr(JNIEnv *env, jobject obj) {

         jcharArray retArr = (*env)->NewCharArray(env, 5);

         unsigned char src[5] = {'a','b','c','d','e'};

         unsigned short tmpArr[5];

         for (int i = 0; i < 5; i++){

                  tmpArr[i] = src[i];

         }

        

         (*env)->SetCharArrayRegion(env, retArr, 0, 5,tmpArr);

         

         return retArr;

}

위 코드에서, unsigend char형 src배열값을, 다시 unsigend short형 배열로 옮긴 후(src -> tmpArr), SetCharArrayRegion을 이용해서 jcharArray에 옮기고 있다. 불필요하게 보이는 중간과정이 더 들어 갔다. 이것은 SetCharArrayRegion에 2바이트 크기의 배열변수를 넣어야하는데, unsigend char형의 크기가 1바이트여서, 2바이트 크기 변수인 unsigend short로 변환해 준 것이다.

 

이런 유형으로 처리를 해줘야할 것이, 표에서 빨간색으로  되어 있는 부분이다.

 

 

번호

Storage size

C 언어

JNI Type

java type

 2

1

unsigned char[]

jcharArray

char[]

 4

2

unsigned short[]

jcharArray

int[]

 6

4(2)

unsigned int[]

jlongArray

long[]

 8

4

unsigned long[]

jlongArray

long[]

 

 

 

이제, 여러 변수형태의 배열에 대한 처리 예제를 작성해 보자.

 

단계1. JNI선언이 포함된 자바 코딩

앞 장의 '맛보기' 예제에서 사용했던 JNITest프로젝트를 그대로 이용하고, Class만 새로운 것으로 생성

   - package: ex

   - Class Name: Ex3_ArrayReturned

 

작성된 코드는 다음과 같다.

package ex;

 

public class Ex3_ArrayReturned {

         public native byte[] returnedCharArr(); //1

         public native char[] returnedUnsignedCharArr(); //2 

         public native short[] returnedShortArr(); //3

         public native int[] returnedUnsignedShortArr(); //4

         public native int[] returnedIntArr();       //5

         public native long[] returnedUnsignedIntArr(); //6  

         public native int[] returnedLongArr(); //7

         public native long[] returnedUnsignedLongArr(); //8

         public native float[] returnedFloatArr(); //9

         public native double[] returnedDoubleArr(); //10

         public native long[] returnedLongLongArr(); //11    

         static {

                  System.loadLibrary("JNI_Hello");

         }

        

         public static void main(String[] args) {

                  new Ex3_ArrayReturned();

         }

        

         public Ex3_ArrayReturned(){

                  byte[] b = returnedCharArr();//1           

                  char[] uc = returnedUnsignedCharArr(); //2

                  short[] s = returnedShortArr(); //3

                  int[] us = returnedUnsignedShortArr(); //4          

                  int[] i_int = returnedIntArr(); //5

                  long[] l = returnedUnsignedIntArr(); //6            

                  int[] i_long = returnedLongArr(); //7               

                  long[] ul = returnedUnsignedLongArr(); //8          

                  float[] f = returnedFloatArr(); //9

                  double[] d = returnedDoubleArr(); //10

                  long[] ll = returnedLongLongArr(); //11    

                 

                  int i;

                  System.out.print("\nb:");

                  for(i=0;i<b.length;i++) System.out.print(b[i]+" ");

                                  

                  System.out.print("\nuc:");

                  for(i=0;i<uc.length;i++) System.out.print(uc[i]+" ");

                 

                  System.out.print("\ns:");

                  for(i=0;i<s.length;i++) System.out.print(s[i]+" ");

                 

                  System.out.print("\nus:");

                  for(i=0;i<us.length;i++) System.out.print(us[i]+" ");

                 

                  System.out.print("\ni_int:");

                  for(i=0;i<i_int.length;i++) System.out.print(i_int[i]+" ");

                 

                  System.out.print("\nl:");

                  for(i=0;i<l.length;i++) System.out.print(l[i]+" ");

                                  

                  System.out.print("\ni_long:");

                  for(i=0;i<i_long.length;i++) System.out.print(i_long[i]+" ");

                 

                  System.out.print("\nul:");

                  for(i=0;i<ul.length;i++) System.out.print(ul[i]+" ");

                 

                  System.out.print("\nf:");

                  for(i=0;i<f.length;i++) System.out.print(f[i]+" ");

                 

                  System.out.print("\nd:");

                  for(i=0;i<d.length;i++) System.out.print(d[i]+" ");

                 

                  System.out.print("\nll:");

                  for(i=0;i<ll.length;i++) System.out.print(ll[i]+" ");                 

         }       

}

 

단계2. javah를 이용해서 C용 헤더파일 생성

JNITest 프로젝트의 bin폴더에서, 아래와 같이 실행

 

 

javah ex.Ex3_ArrayReturned

 

 

단계3. 헤더파일에 선언된 C언어용 함수 작성

앞 장 '맛보기' 예제에서 사용했던 Visual Studio 프로젝트인 'JNi_Hello'를 그대로 이용하겠다. 추가로 C파일을 생성해서 아래와 같이 코딩. 

  - JNI_Hello프로젝트의 '소스 파일'을 선택하고 마우스 우클릭해서 '추가/새항목' 한 후, 이름이 array_returned.c 파일 생성

  - 위 단계2에서 생성된 헤더파일(E:\Workspace-Eclipse\JNITest\bin\ex_Ex3_ArrayReturned.h)에 선언된 함수들 구현

 

구현된 array_returned.c파일은 다음과 같다.

#include <stdio.h>

#include <jni.h>

#include "ex_Ex3_ArrayReturned.h"

 

JNIEXPORT jbyteArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedCharArr(JNIEnv *env, jobject obj){

         jbyteArray retArr = (*env)->NewByteArray(env, 5);

         char tmpArr[5] = { -1, -2, -3, -4, -5 };

 

         (*env)->SetByteArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

 

JNIEXPORT jcharArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedUnsignedCharArr(JNIEnv *env, jobject obj) {

         jcharArray retArr = (*env)->NewCharArray(env, 5);

         unsigned char src[5] = {'a','b','c','d','e'};

         unsigned short tmpArr[5];

         for (int i = 0; i < 5; i++){

                  tmpArr[i] = src[i];

         }

        

         (*env)->SetCharArrayRegion(env, retArr, 0, 5,tmpArr);        

         return retArr;

}

 

JNIEXPORT jshortArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedShortArr(JNIEnv *env, jobject obj) {

         jshortArray retArr = (*env)->NewShortArray(env, 5);

         short tmpArr[5] = { -15000, -15001, -15002, -15003, -15004 };

 

         (*env)->SetShortArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jintArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedUnsignedShortArr(JNIEnv *env, jobject obj) {

         jintArray retArr = (*env)->NewIntArray(env, 5);

         unsigned short src[5] = { 50001, 50002, 50003, 50004, 50005 };

         int tmpArr[5];

         for (int i = 0; i < 5; i++){

                  tmpArr[i] = (int)src[i];

         }

 

         (*env)->SetIntArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jintArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedIntArr(JNIEnv *env, jobject obj) {

         jintArray retArr = (*env)->NewIntArray(env, 5);

         int tmpArr[5] = { -115000, -115001, -115002, -115003, -115004 };

 

         (*env)->SetIntArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jlongArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedUnsignedIntArr(JNIEnv *env, jobject obj) {

         jlongArray retArr = (*env)->NewLongArray(env, 5);

         unsigned int src[5] = { 9115000, 9115001, 9115002, 9115003, 9115004 };

         long long tmpArr[5];

         for (int i = 0; i < 5; i++){

                  tmpArr[i] = src[i];

         }

 

         (*env)->SetLongArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jintArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedLongArr(JNIEnv *env, jobject obj) {

         jintArray retArr = (*env)->NewIntArray(env, 5);

         long tmpArr[5] = { 9115000, 9115001, 9115002, 9115003, 9115004 };

 

         (*env)->SetIntArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jlongArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedUnsignedLongArr(JNIEnv *env, jobject obj) {

         jlongArray retArr = (*env)->NewLongArray(env, 5);

         unsigned long src[5] = { 9115000, 9115001, 9115002, 9115003, 9115004 };

         long long tmpArr[5];

         for (int i = 0; i < 5; i++){

                  tmpArr[i] = src[i];

         }

         (*env)->SetLongArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jfloatArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedFloatArr(JNIEnv *env, jobject obj) {

         jfloatArray retArr = (*env)->NewFloatArray(env, 5);

         float tmpArr[5] = { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f };

 

         (*env)->SetFloatArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jdoubleArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedDoubleArr(JNIEnv *env, jobject obj) {

         jdoubleArray retArr = (*env)->NewDoubleArray(env, 5);

         double tmpArr[5] = { 100000.1, 100000.2, 100000.3, 100000.4, 100000.5 };

 

         (*env)->SetDoubleArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

JNIEXPORT jlongArray JNICALL Java_ex_Ex3_1ArrayReturned_returnedLongLongArr(JNIEnv *env, jobject obj) {

         jlongArray retArr = (*env)->NewLongArray(env, 5);

         long long tmpArr[5] = { 9999990, 9999991, 9999992, 9999993, 9999994 };

 

         (*env)->SetLongArrayRegion(env, retArr, 0, 5, tmpArr);

 

         return retArr;

}

 

 소스작성이 완료되면 F6을 눌러 빌드한다.

 

단계 4. C언어로 제작된 라이브러리파일 이용하는 자바코딩 완성

단계1에서 이미 완성되었고, Eclipse에서 Ctrl-F11을 눌러 Ex3_ArrayReturned클래스를 실행해보면, 아래와 같은 결과가 나올 것이다.

 


b:-1 -2 -3 -4 -5
uc:a b c d e
s:-15000 -15001 -15002 -15003 -15004
us:50001 50002 50003 50004 50005
i_int:-115000 -115001 -115002 -115003 -115004
l:9115000 9115001 9115002 9115003 9115004
i_long:9115000 9115001 9115002 9115003 9115004
ul:9115000 9115001 9115002 9115003 9115004
f:1.1 1.2 1.3 1.4 1.5
d:100000.1 100000.2 100000.3 100000.4 100000.5
ll:9999990 9999991 9999992 9999993 9999994

 

 

  -끝-

 

 

 

 

 

반응형