프로그래밍/Java

JNI(Java Native Interface) 사용 방법 (1/6) - 절차, 맛보기

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

자바에서 다른 언어로 작성된 코드를 실행시키기 위해 사용되는 것이 JNI인데, 작성하는게 꽤 복잡하다.

여기서는, OS는 Windows, Eclipse를 이용해서 자바 개발, Visual Studio 2013을 이용해서 C 코딩하는 것을 기준으로해서, JNI 작성에 대해 종합 정리해보고자 한다.

 

작성 환경

  - OS: Windows 7 64비트

  - Java 개발 툴: Eclipse Luna

  - C 개발 툴: Visual Studio 2013

 

목차

1. 절차

2. 맛보기

3. 예제

   3.1 기본형 변수에 대한 반환이 있는 경우(Java <- C)

   3.2 기본형 변수를 파라미터로 보내는 경우(Java -> C)

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

   3.5 자바의 인스턴스 변수에 대한 읽기/쓰기
   3.6 struct형태의 데이터를 자바로 리턴하기

   3.7 기타

 

1. 절차

JNI코딩을 하려면, 꽤 복잡한 과정을 거쳐야 하기에, 절차를 구분해서 기억해 두면 좋다. 여러가지 구분 방법이 있겠지만, 크게 다음과 같이 4단계 절차가 필요하다.

 

 

 단계

설명 

output 

 1

 JNI선언이 포함된 자바 코딩

java 파일 

 2

 javah를 이용해서 C용 헤더파일 생성

h 파일 

 3

 헤더파일에 선언된 C언어용 함수 작성

c 파일, DLL 파일 

 4

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

dll 파일, class 파일

 

2. 맛보기

맛보기용 간단 JNI코딩을, 앞에서 얘기한 각 절차에 따라 작성해 보면, 각 절차가 어떤것을 의미하는 지 이해가 될 것이다.

 

맛보기용 프로그램

Java프로그램을 실행 시키면콘솔 화면에 Hello World를 출력하는 간단 프로그램. 단, 이 때, "Hello World"의 출력은 C 코딩으로 작성된 DLL에서 수행 

 

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

C로 작성된 코드에서 수행될 메서드를 정의하는 단계이다. 예를 들어, "Hello World"라는 메시지를 출력하는 네이티브 함수가 필요하다면, 자바로 다음과 같이 코딩한다.

public native void getHello();

static{

       System.loadLibrary("JNI_Hello");

}

첫번 째 줄은 getHello라는 메서드가 'native'라는 것을 알려주는 선언이다. 이렇게 선언하면, getHello()가 호출될 때, 자바가 아닌 네이티브로 구현된 메서드를 찾게 된다.

 

그 다음 구문은, 네이티브로 작성된 라이브러리를 지정하는 구문이다. 여기서는 JNI_Hello.dll 이라는 라이브러리를 로딩하고 있다. 이 DLL파일은, 아래에서 설명되는 단계4에서 작성될 것이다.

 

단계1은, Eclipse에서 다음과 같이 하면 된다.

 

1. 프로젝트 생성

    - 프로젝트 이름: JNITest

2. 패키지, 클래스 생성

    - src폴더 밑에서, package 생성: simple

    - simple패키지 밑에 Hello 클랫스 생성

3. Hello 클래스 코딩

package simple;

 

public class Hello {

       public native void getHello();

 

       static {

             System.loadLibrary("JNI_Hello");

       }

 

       public Hello() {

             getHello();

       }

      

       public static void main(String[] args) {

             new Hello();

       }     

}

 

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

위 단계1에서 Hello클래스에 대한 코딩을 했고, Eclipse에서는 Ctrl-S를 눌러 저장을 하면 자동으로 클래스파일이 생성된다.

위 JNITest프로젝트가 있는 폴더를 가보자. (프로젝트 폴더가 "e:\workspace-eclipse\JNITest"폴더라고 가정)

 

프로젝트 폴더의 bin\simple 폴더 밑에 보면 Hello.class가 생성되었을 것이다. javah.exe 프로그램을 이용해서 Hello.class로부터 C용 헤더파일을 생성하자. 이 헤더파일을 이용해서 C 코딩을 하게될 것이다.

 

다음과 같이 하면된다.

  -  윈도우즈의 cmd창 띄운다.

  - JNITest 프로젝트의 bin폴더로 이동

     cd e:\workspace-eclipse\JNITest\bin

  - javah명령어 타이핑: javah simple.Hello

     e:\workspace-eclipse\JNITest\bin>javah simple.Hello

 

위와같이 하면 아래와 같은 내용의 simple_Hello.h 파일이 생성될 것이다.

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class simple_Hello */

 

#ifndef _Included_simple_Hello

#define _Included_simple_Hello

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     simple_Hello

 * Method:    getHello

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_simple_Hello_getHello

  (JNIEnv *, jobject);

 

#ifdef __cplusplus

}

#endif

#endif

위 헤더파일을 C로 코딩할 때 그대로 사용할 것이고, 중간에 선언된 JNIEXPORT void JNICALL Java_simple_Hello_getHello(JNIEnv *, jobject) 함수를 구현할 것이다. (이름을 보면 Java로 시작하고, 패키지_클래스_메서드명 순으로 이름이 자동생성되었음을 알 수 있다.)

 

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

위 단계2에서 자동생성된 헤더파일에 선언되어 있는 함수를 C언어로 아래와 같이 코딩한다.

 

JNIEXPORT void JNICALL Java_simple_Hello_getHello(JNIEnv *env, jobject obj){

        printf("Hello JNI\n");

        return;

}

 

Visual Studio 2013에서 다음과 같이 하면 된다.

 - "파일/새로만들기"에서 'Visual C++/빈 프로젝트' 선택하고, 프로젝트 이름을 지정하고 '확인' 버튼

    . 이름: JNI_Hello    (단계1에서 load_library할 때의 이름과 동일하게 한다.)

   

- 솔루션탐택기에서 JNI_Hello솔루션을 선택하고 마우스 우클릭해서 '속성' 클릭한 후,

   . 오른쪽 상단편에 있는 '구성 관리자' 버튼을 누르고, '활성 솔루션 플랫폼'에 Win32로 되어 있는 버튼을 누른 후 '새 솔루션 플랫폼'에서 x64선택 후 '확인' (OS가 Win7 64비트이고, 설치되어 있는 JDK가 64비트 버전이어서 x64용 DLL을 생성하고자 이렇게 선택하는 것임. 32비트 OS이고, JDK가 32비트용이면 x32로 그대로 놔둬도 됨)

 

. '구성'을 'Release'로 선택 (즉, 구성=Release, 플랫폼=x64)

. "구성속성/일반"에서 '구성 형식': 동적 라이브러리(.dll)   '출력 디렉토리=e:\Workspace-Eclipse\JNITest"

    --> 출력디렉토리를 자바 코딩 프로젝트 폴더로 지정한 것은, 그 쪽으로 바로 DLL파일이 생성되어 이동되도록 하자는 것.

          이렇게 하지 않고, C 프로젝트의 x64폴더에 생성된 DLL파일을 수동으로 자바프로젝트로 복사해도 무방.

. "구성속성/VC++ 데렉터리"의 '포함 디렉터리'에서 '편집/줄추가' 버튼을 눌서서, 아래의 세 폴더 선택해서 추가 (각자 알맞은 JDK폴더 선택)

       c:\jdk1.7\include  

       c:\jdk1.7\include\win32 

       e:\workspace-eclipse\JNITest   

           -> 이 폴더는 단계2에서 생성된 헤더파일이 저장된 곳임. 이렇게 지정하지 않고, 그 헤더파일을 C코드 위치로 복사해도 됨  

 

 

 

 

 

- '소스 파일' 밑에 Hello.c 파일 생성 (파일이름은 아무거나 해도 상관 없음)

   소스파일 선택하고 마우스 우클릭. '추가/새항목'에서 Visual C++/C++ 파일(.cpp) 선택 후, '이름'을 Hello.c 로 해서 '추가' 버튼

- Hello.c 파일을 다음과 같이 코딩하고 저장

#include <stdio.h>

#include <jni.h>

#include "simple_Hello.h"

 

JNIEXPORT void JNICALL Java_simple_Hello_getHello(JNIEnv *env, jobject obj){

        printf("Hello JNI\n");

        return;

}

- F6 버튼을 눌러 Build하면, e:\Workspace-Eclipse\JNITest 폴더에 JNI_Hello.dll 파일이 생성될 것이다.

 

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

다시 단계1에서 작성한 Eclipse의 JNITest 프로젝트로 와서, Hello 클래스를 실행해 본다. (Ctrl-F11)

Eclipse의 아래편 Console 창에  "Hello JNI"가 보이면 성공!!

 

위 4단계 절차를 거치면서 작성된 것을 도식화하면 다음과 같다.


  

위 단계에서 주의할 점을 다시 짚어 보면,

  - 3번의 헤더파일을 만들 때, javah 명령을 수행하는 폴더는 Hello 프로젝트의 bin 폴더이고, 패키지를 simple로 했으므로, 해당 클래스 이름이 simple.Hello 가 됨에 유의

E:\Worksapce-Eclipse\JNITest\bin>javah simple.Hello

 - 5번을 만들기 위해서 Visual Studio의 세팅을 몇가지 해야 한다.

   . 설치된 OS와 JDK에 맞는 플랫폼 설정(x32 혹은 x64)  

   . include 디렉토리로, <JDK설치폴더>/include, <JDK설치폴더>/include/win32 와, Eclipse 프로젝트의 bin 폴더 추가

   . 구성형식을 DLL파일로. 출력폴더를 Eclipse프로젝트의 루트 폴더로.


위에서 살펴본 바와 같이 Java와 C 소스간에 데이터의 교환이 없는 예제는 비교적 간단하다. (절차는 좀 복잡해도)

문제는 Java에서 C코드로 데이터가가 전달되고(파라미터로), C에서 Java로 데이터가 리턴될 때이다.

이 부분은 다음 장에서 살펴본다.


-끝-

 




 

 

 

반응형