Android
JNI : Java Native Interface
단돈백이원
2022. 5. 17. 08:24
자바 네이티브 인터페이스(Java Native Interface, JNI)는 자바 가상 머신(JVM)위에서 실행되고 있는 자바코드가 네이티브 응용 프로그램(하드웨어와 운영 체제 플랫폼에 종속된 프로그램들) 그리고 C, C++ 그리고 어샘블리 같은 다른 언어들로 작성된 라이브러리들을 호출하거나 반대로 호출되는 것을 가능하게 하는 프로그래밍 프레임워크이다.
- 참고
- 자바에서 Method를 다른 프로그래밍 언어로 구현하는 것을 말한다. 성능 향상이나 하드웨어 제어같은 목적을 위해서 사용한다.
- 하드웨어에 종속된 API 등을 시스템콜로 불러오지 못하는 경우 혹은 성능상 문제로 그렇게 불러와서는 안되는 경우 반드시 필요할 수 있음.
- JNI를 생성하는 단계는 크게 4단계이다.
- JNI를 사용할 java파일을 작성.
- java파일을
javac
로 컴파일 후 클래스파일을javah
를 이용해 c header(.h
)파일을 생성. - 인터페이스 역할을 하는
.h
파일을 include하고 정의된 함수를 구현할 네이티브 소스파일 작성. - 기계어로 변환할 컴파일러( 일반적으로 c컴파일러. gcc등. )에 JNI 헤더파일 인클루드 경로를 지정하고, 네이티브 소스를 컴파일하여 네이티브 라이브러리를 생성한다.
- 생성된 라이브러리를 사용하여 실행하려면 java 실행시 라이브러리 위치를 알려주어야한다.
java -Djava.library.path={라이브러리 위치} NativeMethodTest
- 혹은 스크립트로 실행하는 경우라면 환경변수 갱신하여 실행시에는 굳이 안넣어도됨.
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. # java 환경변수중 라이브러리 path를 현재 위치를 참조하도록 갱신함.
- 파라미터/리턴데이터
- https://realapril.tistory.com/35 를 참고하는게 좋다.
- 기본형 데이터는
javah
가 알아서 변환해 주어서 그거 보고j
로 시작하는 JNI 유닛단위(typedef
로 래핑된 c 기본형)으로 주고받으면 된다.- 혹은
JDK폴더의 include\jni.h와 include\win32\jni_md.h에 아래와 같이 8개변수에 대해 정의되어 있다.
- 단, c에서 데이터를 처리시 JNI 유닛단위로 변환하는 경우 java와 c사이에 기본데이터형이 다르므로 컨버팅에 주의해야함.
- 혹은
- 객체형 데이터는
- 알쓸신잡
javah
를 지원하기 전엔 헤더파일을 일일히 다 작성해줘야 했다고함. 결국 편의성을 위한 도구임.
예시
- `HelloWorld.java` : Native 라이브러리를 참조할 코드를 정의한 java 코드
class HelloWorld
{
static{
System.loadLibrary("HelloWorld"); // 생성된 라이브러리를 System.loadLibrary("")로 불러온다. javah로 생성한 이름이다.
}
private native void print(); // 라이브러리에 정의된 함수를 호출할 수 있도록 native 멤버로 선언한다.
public static void main(String[] args)
{
new HelloWorld().print(); // HelloWorld가 제공하는 native 메서드 print를 호출.
}
}
- `HelloWorld.h` : JNI 라이브러리를 import하려는 java파일을 compile한 결과물인 class파일을 `javah`도구를 이용해 생성한 헤더파일.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print // c 파일에서 구현해야할 함수 정의.
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
- `libHelloWorld.c `: 실제 네이티브 call을 할 c파일. 생성된 header파일에 정의된 함수를 구현해야한다.
#include <stdio.h>
#include "HelloWorld.h" // 인터페이스가 되는 헤더파일 include
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj) // 정의된 인터페이스 구현.
{
printf("Hello World!\n");
return;
}
- `make.sh` : JNI를 사용하는 `class` 및 JNI 라이브러리를 생성할 수 있게끔하는 빌드 자동화 파일.
#!/bin/sh
# openbsd 4.9
# gcc 4.2.1
# openjdk 1.7.0
JAVA_HOME=$(readlink -f /usr/bin/javac | sed "s:bin/javac::")
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. # java 환경변수중 라이브러리 path를 현재 위치를 참조하도록 갱신함.
javac HelloWorld.java # java파일을 컴파일하여 class파일을 생성.
javah HelloWorld # javah : class파일로 JNI용 c header 파일을 생성 할 수 있는 도구.
gcc -I${JAVA_HOME}/include -shared libHelloWorld.c -o libHelloWorld.so # 지정된 컴파일러에 jni 헤더파일 include 경로지정( -I ), 컴파일하여 정적/동적링크파일을 생성할 JNI문법으로 작성된 c파일( .c ) 그리고 생성될 라이브러리 이름 ( -o .so 등 )을 지정하여 컴파일한다.
java HelloWorld # 실행. 위에서 환경변수를 갱신했기 때문에 -D 옵션을 사용하지 않음.
`chmod +x make.sh` `./make.sh`