C/C++로 제작한 모듈을 java에서 불러 쓸 일이 생겨서 어떻게 해야하는지 정리했다.
만약, 불러올 수 있는 동적라이브러리가 있다면 2~5를 생략해도 된다. 단, 메서드 혹은 API를 알고 있어야한다.
1. HelloJni.java 파일 생성
- 라이브러리를 load하는 클래스
class HelloJni{ static { System.loadLibrary("hellojni");} native void printHello(); native void addintiger(int var1, int var2); native int sumIntiger(int[] intData); native byte sumByte(byte[] byteData); native void printString(String str); native void getByteData(byte[] getByte); native int setByteArray(byte[] ptData); native byte[] makeByteArray(int len); native byte[] makeNsetBArray(int len); } |
2. java컴파일 HelloJni.class 파일 생성
$javac HelloJni.java
==> HelloJni.class파일 생성.
3. HelloJni.h C/C++ 헤더파일 생성.
$javah -jni HelloJni
==> HelloJni.h 파일 생성. 자동으로 만들어지니 참고만 할 것.
/* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class HelloJni */ #ifndef _Included_HelloJni #define _Included_HelloJni #ifdef __cplusplus extern "C" { #endif /* * Class: HelloJni * Method: printHello * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloJni_printHello (JNIEnv *, jobject); /* * Class: HelloJni * Method: addintiger * Signature: (II)V */ JNIEXPORT void JNICALL Java_HelloJni_addintiger (JNIEnv *, jobject, jint, jint); /* * Class: HelloJni * Method: sumIntiger * Signature: ([I)I */ JNIEXPORT jint JNICALL Java_HelloJni_sumIntiger (JNIEnv *, jobject, jintArray); /* * Class: HelloJni * Method: sumByte * Signature: ([B)B */ JNIEXPORT jbyte JNICALL Java_HelloJni_sumByte (JNIEnv *, jobject, jbyteArray); /* * Class: HelloJni * Method: printString * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_HelloJni_printString (JNIEnv *, jobject, jstring); /* * Class: HelloJni * Method: getByteData * Signature: ([B)V */ JNIEXPORT void JNICALL Java_HelloJni_getByteData (JNIEnv *, jobject, jbyteArray); /* * Class: HelloJni * Method: setByteArray * Signature: ([B)I */ JNIEXPORT jint JNICALL Java_HelloJni_setByteArray (JNIEnv *, jobject, jbyteArray); /* * Class: HelloJni * Method: makeByteArray * Signature: (I)[B */ JNIEXPORT jbyteArray JNICALL Java_HelloJni_makeByteArray (JNIEnv *, jobject, jint); /* * Class: HelloJni * Method: makeNsetBArray * Signature: (I)[B */ JNIEXPORT jbyteArray JNICALL Java_HelloJni_makeNsetBArray (JNIEnv *, jobject, jint); #ifdef __cplusplus } #endif #endif
|
3.1 Class를 찾을 수 없다고 나올 때.
- classpath를 설정하라는 조언과 package의 전체 이름을 입력하라는 조언 둘다 안됨.
- src 폴더로 내려가서 package이름으로 실행하니 성공.
3.2 헤더를 생성하는 경로에 따라 함수이름이 달라짐.
4. 생성된 헤더파일에 맞춰 C/C++ 파일 생성.
==> 헤더파일의 메서드/API만 복사하여 내용을 채워넣음.
==> Falinux 포럼의 JNI 배열관련 글을 참고.
http://forum.falinux.com/zbxe/index.php?document_srl=570993&mid=lecture_tip
#include "HelloJni.h"
#include <stdio.h> #include <jni.h> #include <stdlib.h> #ifdef __cplusplus extern "C" { #endif
JNIEXPORT void JNICALL Java_HelloJni_printHello(JNIEnv *env, jobject jobj)
{ printf("Test Jni process\n"); }
JNIEXPORT void JNICALL Java_HelloJni_addintiger(JNIEnv *env, jobject jobj, jint var1, jint var2) { printf("%d + %d = %d\n",var1,var2,var1+var2); }
JNIEXPORT jint JNICALL Java_HelloJni_sumIntiger(JNIEnv *env, jobject jobj, jintArray intData) { jint *intBuf; int size; int sum, i; size = env->GetArrayLength(intData); intBuf = env->GetIntArrayElements(intData, NULL); sum = 0; for(i=0 ; i < size ; i++){ sum += intBuf[i]*10; printf("array[%d] = %d, sum = %d\n",i,intBuf[i],sum); } env->ReleaseIntArrayElements(intData, intBuf, 0); return sum; }
JNIEXPORT jbyte JNICALL Java_HelloJni_sumByte(JNIEnv *env, jobject jobj, jbyteArray byteData) { jbyte *byteBuf; int size, i; char sum; size = env->GetArrayLength(byteData); byteBuf = env->GetByteArrayElements(byteData, NULL); sum = 0; for(i=0 ; i < size ; i++){ sum += byteBuf[i]; printf("array[%d] = %d, sum = %d\n",i,byteBuf[i],sum); } env->ReleaseByteArrayElements(byteData, byteBuf, 0); return sum; }
JNIEXPORT void JNICALL Java_HelloJni_printString(JNIEnv *env, jobject jobj, jstring jstr) { const char *str = env->GetStringUTFChars(jstr,NULL); printf("String is[%s]\n",str); env->ReleaseStringUTFChars(jstr,str); }
JNIEXPORT void JNICALL Java_HelloJni_getByteData(JNIEnv *env, jobject jobj, jbyteArray byteData) { jbyte *byteBuf; int size, i; size = env->GetArrayLength(byteData); byteBuf = env->GetByteArrayElements(byteData, NULL); for(i=0 ; i < size ; i++){ byteBuf[i] = i*2; } env->SetByteArrayRegion(byteData, 0, 10, (const jbyte *)byteBuf); env->ReleaseByteArrayElements(byteData, byteBuf, 0); }
JNIEXPORT jint JNICALL Java_HelloJni_setByteArray(JNIEnv *env, jobject jobj, jbyteArray byteData) { jint mallocSize; jbyte *byteBuf; int i;
mallocSize = 20;
byteBuf = env->GetByteArrayElements(byteData, NULL); for(i=0 ; i < mallocSize ; i++){ byteBuf[i] = i*3; } env->SetByteArrayRegion(byteData, 0, mallocSize, (const jbyte *)byteBuf); return mallocSize; }
JNIEXPORT jbyteArray JNICALL Java_HelloJni_makeByteArray(JNIEnv *env, jobject jobj, jint len) { jbyteArray bytePt = NULL; jbyte *byteBuf; int i;
bytePt = env->NewByteArray(len);
if(bytePt != NULL){ printf("make new byte array\n"); return bytePt; } else{ printf("make fail\n"); return NULL; } }
JNIEXPORT jbyteArray JNICALL Java_HelloJni_makeNsetBArray(JNIEnv *env, jobject jobj, jint len) { jbyteArray bytePt = NULL; jbyte *byteBuf; int i;
bytePt = env->NewByteArray(len);
if(bytePt != NULL){ printf("make new byte array\n");
byteBuf = (jbyte *)malloc(sizeof(jbyte)*len); for(i=0 ; i < len ; i++){ byteBuf[i] = i*4; } env->SetByteArrayRegion(bytePt, 0 , len, (const jbyte *)byteBuf); free(byteBuf); return bytePt; } else{ printf("make fail\n"); return NULL; } }
#ifdef __cplusplus } #endif
|
5. 라이브러리 생성.
5-1. linux 64bits
$gcc -m64 -fPIC -shared -o libhellojni.so HelloJni.cpp -I/usr/lib/java/jdk/include -I/usr/lib/java/jdk/include/linux
5-2. linux 32bits
$gcc -m32 -shared -o libhellojni.so HelloJni.cpp -I/usr/lib/java/jdk/include -I/usr/lib/java/jdk/include/linux
5-3. 라즈베리파이등 (coretex-arm)
==> 크로스컴파일러가 없다면 "apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf"
$arm-linux-gnueabihf-g++ -fPIC -shared -o libhellojni.so HelloJni.cpp -I/usr/lib/java/jdk/include -I/usr/lib/java/jdk/include/linux
$readelf -hA libhellojni.so
Tag_CPU_arch: v7
5-4. arm코어가 다른 보드.
==> 크로스컴파일러가 없다면 "apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi"
$arm-linux-gnueabi-g++ -shared -o libhellojni.so HelloJni.cpp -I/usr/lib/java/jdk/include -I/usr/lib/java/jdk/include/linux
$readelf -hA libhellojni.so
Tag_CPU_arch: v5T
6. 동적라이브러리 복사.
$sudo cp libhellojni.so /usr/lib/
6-1. 동적라이브러리 파일 복사를 안하려면, 동적라이브러리 PATH를 설정하는 방법도 있다. 아래 파일의 제일 밑에 LD_LIBRARY_PATH를 추가해서 잘되긴 했는데, 타겟보드에서는 실패했다.
$vi /etc/profile
export LD_LIBRARY_PATH=/home/user_name/mylib:${LD_LIBRARY_PATH}