Java의 장점중 하나는 JVM(가상머신) 위에서 실행되기 때문에 특정 운영체제에 종속되지 않는것이다. 운영체제에 맞는 JVM이 존재하기 때문에 Java로 코딩을 할 때는 운영체제에 상관없이 원하는 결과물을 얻을 수 있다. (JVM은 플랫폼에 상관없이 자바 코드(class파일)를 읽고 실행한다.)
하지만 운영체제의 모든 기능을 JVM이 담지 못하기 때문에 구현하고자 하는 몇몇 기능들은 Java로 해결되지 않는 경우가 존재하고 이런 경우에 사용할 수 있는 방법중 하낙 JNI이다.
JNI(Java Native Interface)는 Java로 구현하기 힘든 기능에 대해 Native Code(CPU와 운영체제가 직접 실행할 수 있는 코드, C/C++)을 사용할 수 있는 기능이다. (Java와 다른 언어를 연동하는 기능)
또한 Native Code로 작성된 기존 라이브러리를 Java로 다시 작성하는 대신 재사용하기 위해 사용하기도 한다.
이렇듯 JNI의 장점이 여럿 있지만, Native Code를 이용하게 됨으로 특정 아키텍쳐나 플랫폼에 종속될 수밖에 없다.
package com.test.jni;
public class JNITest {
// 1. native method 선언 (C/C++로 작성된, 호출할 함수 선언)
private native void PrintHelloWorld();
static {
// 2. 윈도우의 경우 JNITest.dll, 리눅스의 경우 JNITest.so 파일을 로드
// 일반적으로 환경변수에 경로를 설정하고, 설정된 경로를 불러와서 LoadLibrary한다
System.loadLibrary("JNITest");
}
public static void main(String[] args) {
JNITest jni = new JNITest();
jni.PrintHelloWorld(); // 3. native method 호출
}
}
(1) Native Method 선언
Java는 메서드 구현이 Native Code에서 제공될 것임을 나타내는 데 사용되는 native 키워드를 제공한다. C/C++로 작성할 함수에 대해 (Java에서 호출할 함수) native키워드를 사용하여 함수를 선언한다.
(2) LoadLibrary
static 블럭 안에서 사용할 라이브러리 파일(dll 또는 so)을 로드한다. 일반적으로 java.library.path를 설정하여 불러올 파일의 경로를 지정한다.
별도의 경로를 지정하지 않은 경우 프로젝트 폴더에서(src폴더 상위) 해당 라이브러리 파일(JNITest.dll 또는 JNITest.so)을 찾는다.
(3) native method 호출하기
JNITest 객체를 생성하고 native method를 호출한다.
(4) 헤더파일 만들기 (javah)
위에서 작성한 JNITest.java를 컴파일한다.
cmd를 실행하고 프로젝트 폴더 밑에 bin 폴더로 이동후 아래 명령어를 입력한다.
javah [패키지명].[클래스명]
(ex, javah com.test.jni.JNITest)
오류없이 실행된다면 bin폴더에 [패키지명]_[클래스명].h
(ex, com_test_jni_JNITest.h) 파일이 생성된다.
// com_test_jni_JNITest.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_jni_JNITest */
#ifndef _Included_com_test_jni_JNITest
#define _Included_com_test_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_test_jni_JNITest
* Method: PrintHelloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_test_jni_JNITest_PrintHelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
(1) DLL프로젝트 생성 및 설정
JNITest라는 이름의 DLL프로젝트를 생성하고 '프로젝트 속성'-'VC++ 디렉터리'-'포함 디렉터리'에 아래의 경로를 추가한다.
[JDK 설치 경로]\include; [JDK 설치 경로]\include\win32
ex) C:\Program Files (x86)\Java\jdk1.7.0_80\include; C:\Program Files (x86)\Java\jdk1.7.0_80\include\win32;
(2) 헤더파일 추가
javah로 만든 헤더 파일을 C++ 소스파일이 있는 폴더에 복사하고 프로젝트에 추가한다.
(3) 소스파일 작성
dllmain.cpp 파일의 이름을 JNITest.cpp로 변경한다.
javah로 만든 헤더파일의 함수 헤더를 복사하고 몸체를 작성한다.
#include "com_test_jni_JNITest.h"
JNIEXPORT void JNICALL Java_com_test_jni_JNITest_PrintHelloWorld
(JNIEnv*, jobject)
{
printf("Hello World~!");
}
C/C++ 프로젝트를 컴파일 하면 JNITest.dll 파일이 생성된다.
해당 DLL파일을 라이브러리 패스로 지정한 곳에 복사한다. (별도로 라이브러리 패스를 지정하지 않았다면 이클립스 프로젝트 폴더에 복사한다.)
JNITest.java를 실행하면 Console창에 Hello Wordl~!
문자열이 출력된다.
다음 포스팅에서 JNI에 대해 더 자세히 알아보도록 하겠다.
헤더파일 생성 단계에서 java 11 부터는 javah가 없습니다 javac의 -h 옵션으로 하셔야합니다