IT관련 지식들 2019. 9. 27. 11:00

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}

 

posted by 동글동글82
: