프로그래밍/Java

JNI(Java Native Interface) 이용 방법 (4/6) - 3.4 ArrayParameter

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

3.4 배열값을 파라미터로 전송(배열값: Java->C)

이번 챕터에서는 자바에서의 배열값을 C로 전달하는 예제이다.

 

예를 들어, 아래와 같은 네이티브 메서드를 Java에서 선언했다고 생각해보자.

public native int sum(byte[] b);

 

이에 대한 C코드는 아래와 같이 구현될 수 있다..

JNIEXPORT jint JNICALL Java_ex_Ex4_1ArrayParameter_sum___3B(JNIEnv *env, jobject obj, jbyteArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jbyte *byteArray = (*env)->GetByteArrayElements(env, arr, NULL);

         if (byteArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         int result = 0;

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

                  result += byteArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseBooleanArrayElements(env, arr, byteArray, 0);

 

         return result;

}

 위 코드에서와 같이, Java로부터 배열값을 건네 받아서 C에서 처리하는 루틴을 크게 3단계로 나눌 수 있다.

Step1은, 파라미터로 받은 배열값을 C에서의 배열형태로 변환하는 과정이다. 이 때 GetxxxArrayElement 함수를 사용하게 되는데, 각 변수 형태에 맞는 함수를 사용하면 된다.

 

jboolean * (JNICALL *GetBooleanArrayElements) (JNIEnv *env, jbooleanArray array, jboolean *isCopy);

jbyte * (JNICALL *GetByteArrayElements) (JNIEnv *env, jbyteArray array, jboolean *isCopy);

jchar * (JNICALL *GetCharArrayElements) (JNIEnv *env, jcharArray array, jboolean *isCopy);

jshort * (JNICALL *GetShortArrayElements) (JNIEnv *env, jshortArray array, jboolean *isCopy);

jint * (JNICALL *GetIntArrayElements) (JNIEnv *env, jintArray array, jboolean *isCopy);

jlong * (JNICALL *GetLongArrayElements) (JNIEnv *env, jlongArray array, jboolean *isCopy);

jfloat * (JNICALL *GetFloatArrayElements) (JNIEnv *env, jfloatArray array, jboolean *isCopy);

jdouble * (JNICALL *GetDoubleArrayElements) (JNIEnv *env, jdoubleArray array, jboolean *isCopy);

이 함수들에 의해서 자바의 배열이 C형태의 배열로 매핑되는데, 매핑 유효기간은 ReleasexxxArrayElements()함수가 호출될 때까지다. 따라서, Java의 배열값을 읽으려면 매핑된 C 배열값을 읽으면 되고, 또한 C 배열값을 바꾸면 Java의 배열값도 바뀌게 된다.

여기서, isCopy를 true로 하면, C에서 생성되는 배열이 Java와 다이렉트 연결되는 것이 아니라, 새로운 배열로 생성되고 자바의 배열값이 copy되는 구조로, C의 배열값을 바꾸더라도 바로 자바배열값에 영향을 미치지 않다가 ReleaseXXXArrayElements()가 호출되면 그때라야 수정사항이 바뀌게 된다. (여기서는 그냥 NULL로 지정해서 사용하기로 하자)

 

또한, 자바 배열의 특징인 '배열의 크기'정보도 가져올 수 있는데, GetArrayLength() 함수를 사용하면 된다.

jsize (JNICALL *GetArrayLength) (JNIEnv *env, jarray array);

 

Step2에서는 생성된 C 배열을 사용하는 단계이다. C에서의 일반적인 배열처럼 사용하면 된다.

 

Step3는 생성된 매핑용 C 배열을 해제하는 것으로, ReleaseXXXArrayElements 함수를 사용하면 된다.

void (JNICALL *ReleaseBooleanArrayElements)

      (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode);

void (JNICALL *ReleaseByteArrayElements)

      (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);

void (JNICALL *ReleaseCharArrayElements)

      (JNIEnv *env, jcharArray array, jchar *elems, jint mode);

void (JNICALL *ReleaseShortArrayElements)

      (JNIEnv *env, jshortArray array, jshort *elems, jint mode);

void (JNICALL *ReleaseIntArrayElements)

      (JNIEnv *env, jintArray array, jint *elems, jint mode);

void (JNICALL *ReleaseLongArrayElements)

      (JNIEnv *env, jlongArray array, jlong *elems, jint mode);

void (JNICALL *ReleaseFloatArrayElements)

      (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode);

void (JNICALL *ReleaseDoubleArrayElements)

      (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);

ReleaseXXXArrayElements 함수는, Java VM(Virtual Machine)에게 더 이상 파라미터로 넘어온 자바배열인 elems에 대한 접근을 하지 않겠다는 것을 알려주는 함수로, NewXXXArrayElements를 사용해서 매핑함수를 만들었다면 반드시 호출해주어야 한다.

  - array: 생성된 C 매핑 배열

  - elems: 파라미터로 넘어온 배열

  - mode: NewXXXArrayElements()의 isCop = true의 경우에 copy를 어떻게 하는지 지정하는 파라미터

    (여기서는 NULL로 해서 사용할 것임. 상세 사항은 https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html 참조)

 

이제 여러 변수별 사용예를 단계별 따라하기로 작성해 보자.

 

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

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

   - package: ex

   - Class Name: Ex4_ArrayParameter

 

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

 

package ex;

 

public class Ex4_ArrayParameter {

         public native boolean xor(boolean[] b);

         public native int sum(byte[] b);

         public native int sum(short[] s);

         public native int sum(char[] c);

         public native long sum(int[] i);

         public native long sum(long[] l);

         public native double sum(float[] f);

         public native double sum(double[] d);

 

         static {

                  System.loadLibrary("JNI_Hello");

         }

        

         public static void main(String[] args) {

                  new Ex4_ArrayParameter();

         }

        

         public Ex4_ArrayParameter(){

                  boolean[] boolArray = {true,false};

                  boolean boolResult = xor(boolArray);

                 

                  byte[] byteArray = {-1,-2,-3,-4,-5};

                  int byteResult = sum(byteArray); //-15

                 

                  short[] shortArray = {-10,-20,-30};

                  int shortResult = sum(shortArray); //-60

                 

                  char[] charArray = {10,20,30}; //60

                  int charResult = sum(charArray);

                 

                  int[] intArray = {1000,2000,3000};

                  long intResult = sum(intArray); //6000

                 

                  long[] longArray = {100000,200000,300000};

                  long longResult = sum(longArray); //600000

                 

                  float[] floatArray = {1.1f,1.2f,1.3f};

                  double floatResult = sum(floatArray); //3.6         

                 

                  double[] doubleArray = {100000.1d,200000.1d,300000.1d};

                  double doubleResult = sum(doubleArray); //699999.3

                 

                  //print

                  System.out.println("boolResult:" + boolResult);

                  System.out.println("byteResult:" + byteResult);

                  System.out.println("shortResult:" + shortResult);

                  System.out.println("charResult:" + charResult);

                  System.out.println("intResult:" + intResult);

                  System.out.println("longResult:" + longResult);

                  System.out.println("floatResult:" + floatResult);

                  System.out.println("doubleResult:" + doubleResult);

                 

                 

         }

 

}

 

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

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

 

 

 

javah ex.Ex4_ArrayParameter

 

 

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

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

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

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

 

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

#include <stdio.h>

#include <jni.h>

#include "ex_Ex4_ArrayParameter.h"

 

JNIEXPORT jboolean JNICALL Java_ex_Ex4_1ArrayParameter_xor(JNIEnv *env, jobject obj, jbooleanArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jboolean *boolArray = (*env)->GetBooleanArrayElements(env, arr, NULL);

         if (boolArray == NULL) {

                  return 0;

         }

        

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         jboolean result = 0;

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

                  result ^= boolArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseBooleanArrayElements(env, arr, boolArray, 0);

        

         return result;

}

 

JNIEXPORT jint JNICALL Java_ex_Ex4_1ArrayParameter_sum___3B(JNIEnv *env, jobject obj, jbyteArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jbyte *byteArray = (*env)->GetByteArrayElements(env, arr, NULL);

         if (byteArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         int result = 0;

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

                  result += byteArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseByteArrayElements(env, arr, byteArray, 0);

 

         return result;

}

 

JNIEXPORT jint JNICALL Java_ex_Ex4_1ArrayParameter_sum___3S(JNIEnv *env, jobject obj, jshortArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jshort *shortArray = (*env)->GetShortArrayElements(env, arr, NULL);

         if (shortArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         int result = 0;

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

                  result += shortArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseShortArrayElements(env, arr, shortArray, 0);

 

         return result;

}

 

JNIEXPORT jint JNICALL Java_ex_Ex4_1ArrayParameter_sum___3C(JNIEnv *env, jobject obj, jcharArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jchar *charArray = (*env)->GetCharArrayElements(env, arr, NULL);

         if (charArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         int result = 0;

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

                  result += charArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseCharArrayElements(env, arr, charArray, 0);

 

         return result;

}

 

JNIEXPORT jlong JNICALL Java_ex_Ex4_1ArrayParameter_sum___3I(JNIEnv *env, jobject obj, jintArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jint *intArray = (*env)->GetIntArrayElements(env, arr, NULL);

         if (intArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         long long result = 0;

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

                  result += intArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseIntArrayElements(env, arr, intArray, 0);

 

         return result;

}       

 

JNIEXPORT jlong JNICALL Java_ex_Ex4_1ArrayParameter_sum___3J(JNIEnv *env, jobject obj, jlongArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jlong *longArray = (*env)->GetLongArrayElements(env, arr, NULL);

         if (longArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         long long result = 0;

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

                  result += longArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseLongArrayElements(env, arr, longArray, 0);

 

         return result;

}

 

JNIEXPORT jdouble JNICALL Java_ex_Ex4_1ArrayParameter_sum___3F(JNIEnv *env, jobject obj, jfloatArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jfloat *floatArray = (*env)->GetFloatArrayElements(env, arr, NULL);

         if (floatArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         float result = 0;

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

                  result += floatArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseFloatArrayElements(env, arr, floatArray, 0);

 

         return (double)result;

}

 

JNIEXPORT jdouble JNICALL Java_ex_Ex4_1ArrayParameter_sum___3D(JNIEnv *env, jobject obj, jdoubleArray arr){

         //Step1. Parameter 받은 java array C언어의 array 형태로 변환. 길이 정보도 받아냄

         jdouble *doubleArray = (*env)->GetDoubleArrayElements(env, arr, NULL);

         if (doubleArray == NULL) {

                  return 0;

         }

         jsize len = (*env)->GetArrayLength(env, arr);

 

         //Step2. 배열값에 대한 연산 수행

         double result = 0;

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

                  result += doubleArray[i];

         }

 

         //Step3. Release array resources

         (*env)->ReleaseDoubleArrayElements(env, arr, doubleArray, 0);

 

         return result;

}

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

 

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

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

 

 

boolResult:true
byteResult:-15
shortResult:-60
charResult:60
intResult:6000
longResult:600000
floatResult:3.6000001430511475
doubleResult:600000.3
ddd:3.6000001

 

 위 결과에서 floatResult값이 3.6이 아닌것은 float변수의 정밀도 한계에 의해 생긴 것임.

-끝-

반응형