혼자 잡담) java-jni-cpp 가지고 300만원짜리 알바까지 했는데..
왠걸 다시 하니 기억이 안난다. 오홋 새록새록해~ 그래도 어떻게 하나 열심히 공부해야지~^^




정리하는 내용
- 리눅스에서 java->c 호출
- 리눅스에서 c->java 호출


그래픽이나 c/c++로 만들어진 라이브러리들을 바로 사용할 수 있도록 하는 프레임웤이다.

HelloJNI 테스트 해본다.

class HelloJNI {
 native void printHello();
 native void printString(String str);
 
 static {
  System.load("/work/JniTest/hellojni.so");
 }
 public static void main(String[] args) {
  HelloJNI myJNI = new HelloJNI();
  myJNI.printHello();
  myJNI.printString("Hello in C");
 }
}



삼바를 이용해서 리눅스 /work/JniTest 디렉토리를 생성하고,  HelloJNI.java 복사한다.

컴파일하고, 그  class 파일을 이용해서 header 파일을 생성하도록 한다.
javac HelloJNI.java
javah HelloJNI


HelloJNI.h 파일이 생성

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* 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:    printString
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_HelloJNI_printString
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif






HelloJNI.c 소스를 생성

#include "HelloJNI.h"

JNIEXPORT void JNICALL Java_HelloJNI_printHello(JNIEnv *env, jobject obj) {
 printf("Hello World !!! jni\n");
}


JNIEXPORT void JNICALL Java_HelloJNI_printString(JNIEnv *env, jobject obj, jstring string) {
  const char *str = (*env)->GetStringUTFChars(env,string,0);
  printf("%s\n", str);
  return;
}





컴파일을 하여 so 파일이 만들어지도록 한다.

# gcc -c -I$JAVA_HOME/include -I$JAVA_HOME/include/linux HelloJNI.c
# gcc -shared -o HelloJNI.so HelloJNI.o
# ls -al HelloJNI.so
HelloJNI.so

 정상적으로 동작된다.

# java HelloJNI
Hello World !!!
Hello in C!!!

 


이런식으로 표현한 다양한 방식을 샘플로 적어본다.

java 코드

class JniFuncMain {
..
public static native JniTest createJniObject();
..
}

 

class JniTest
{
 private int intField;
 
 public JniTest(int num)
 {
  intField = num;
 }
 
 public int callByNative(int num)
 {
  return num;
 }
 
 public void callTest()
 {
  System.out.println("intField=" + intField);
 }
}



 cpp 코드


#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniFuncMain
 * Method:    createJniObject
 * Signature: ()LJniTest;
 */
JNIEXPORT jobject JNICALL Java_JniFuncMain_createJniObject
  (JNIEnv *env, jclass clazz)
{
 jclass targetClass;
 jmethodID mid;
 jobject newObject;
 jstring helloStr;
 jfieldID fid;
 jint staticIntField;
 jint result;
 
 // Get the Class staticIntField Value
 fid = env->GetStaticFieldID(clazz, "staticIntField", "I");
 staticIntField = env->GetStaticIntField(clazz, fid);
 printf("[CPP] Get JniFuncMain_Class_staticIntField!!\n");
 printf("[CPP] JniFuncMain_Class_staticIntField = %d\n", staticIntField);
 
 // Find the Class to create object
 targetClass = env->FindClass("JniTest");
 
 // Find the Constructor
 mid = env->GetMethodID(targetClass, "<init>", "(I)V");
 
 // Create a JniTest Object
 printf("[CPP] JniTest_Object Create!!\n");
 newObject = env->NewObject(targetClass, mid, 100);
 
 // Call the Method of Object
 mid = env->GetMethodID(targetClass, "callByNative", "(I)I");
 result = env->CallIntMethod(newObject, mid, 200);
 
 // Set the intField_field of JniObject
 fid = env->GetFieldID(targetClass, "intField", "I");
 env->SetIntField(newObject, fid, result);
 
 // return created Object
 return newObject;
}
#ifdef __cplusplus
}
#endif
 


# g++ -I/$JAVA_HOME/include -I/$JAVA_HOME/include/linux -c jnifunc.cpp
# g++ -shared -o jnifunc.so jnifunc.o
# java 메인클래스실행




이외에 c에서 java로도 호출이 가능하게 할 수 있다.


#include <jni.h>

int main()
{
 JNIEnv *env;
 JavaVM *vm;
 JavaVMInitArgs vm_args;
 JavaVMOption options[1];
 jint res;
 jclass cls;
 jmethodID mid;
 jstring jstr;
 jclass stringClass;
 jobjectArray args;
 
 // 1.JVM에 넘겨줄 아규먼트 셋팅 
 options[0].optionString = "-Djava.class.path=.";
 vm_args.version = 0x00010002;
 vm_args.options = options;
 vm_args.nOptions = 1;
 vm_args.ignoreUnrecognized = JNI_TRUE;
 
 //2.JVM 생성
 res = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);
 
 //3. 클래스 검색 과 로딩
 cls = (*env)->FindClass(env, "InvocationApiTest");
 
 //4. main 메소드 얻어오기 
 mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
 
 //5.메인메소드의 실제 파라미터값을 지정한다. 
 jstr = (*env)->NewStringUTF(env, "Hello Test from Native!!!");
 stringClass = (*env)->FindClass(env, "java/lang/String");
 args = (*env)->NewObjectArray(env, 1, stringClass, jstr);
 
 //6.main 메소드를 호출한다.
 (*env)->CallStaticVoidMethod(env, cls, mid, args);
 
 //7.JVM를 내린다.
 (*vm)->DestroyJavaVM(vm);
}




클래스 파일

public class InvocationApiTest
{
 public static void main(String[] args)
 {
  System.out.println(args[0]);
 }
}


# gcc -I/$JAVA_HOME/include -I$JAVA_HOME/include/linux -c invocationApi.c

그다음은 jvm 관련 항목을 링크 옵션으로 줘야 jvm이 실행할 수 있다.
# gcc -L$JAVA_HOME/jre/lib/i386/client invocationApi.o -ljvm
(libjvm.so 라는 파일이 $JAVA_HOME/jre/lib/i386/client/libjvm.so 파일이 있어야 한다.)

dependent한 library 가 있는지를 확인한다.

# ldd a.out 

# export LD_LIBRRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386/client

# a.out
결과 출력


다음예는
jni onload 함수를 오버라이딩해서,
a라고 하는 함수를 b라는 함수로 호출되도록 바꿔치기 를 할 수 있는 예제이다.


// hellojnimap.cpp

#include <jni.h>
#include <stdio.h>

void printHelloNative(JNIEnv *env, jobject obj);
void printStringNative(JNIEnv *env, jobject obj, jstring string);

// JNI_ONload는 오버라이딩한 것이다.
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
 JNIEnv *env = NULL;
 JNINativeMethod nm[2];
 jclass cls;
 jint result = -1;
 
 if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
  printf("Error");
  return JNI_ERR;
 }
 
 cls = env->FindClass("HelloJNI");
 
 nm[0].name = (char*)"printHello";
 nm[0].signature = (char*)"()V";
 nm[0].fnPtr = (void *)printHelloNative;
 
 nm[1].name = (char*)"printString";
 nm[1].signature = (char*)"(Ljava/lang/String;)V";
 nm[1].fnPtr = (void *)printStringNative;
 
 env->RegisterNatives(cls, nm, 2);
 
 return JNI_VERSION_1_4;
}

void printHelloNative(JNIEnv *env, jobject obj)
{
 printf("Hello World in C++!!\n");
 return;
}

void printStringNative(JNIEnv *env, jobject obj, jstring string)
{
 //const char *str = (*env)->GetStringUTFChars(env, string, 0);
 const char *str = env->GetStringUTFChars(string, 0);
 printf("%s\n", str);
 return;
}

#if 0
JNIEXPORT void JNICALL Java_HelloJNI_printHello
  (JNIEnv *env, jobject obj)
{
 printf("Hello World in C!!\n");
 return;
}

JNIEXPORT void JNICALL Java_HelloJNI_printString
  (JNIEnv *env, jobject obj, jstring string)
{
 const char *str = (*env)->GetStringUTFChars(env, string, 0);
 printf("%s\n", str);
 return;
}
#endif





//HelloJNI.java

class HelloJNI
{
 native void printHello();
 native void printString(String str);
 
 static {
  System.load("/work/JniTest/hellojnimap.so");
 }
 
 public static void main(String[] args) {
  HelloJNI myJNI = new HelloJNI();
  
  myJNI.printHello();
  myJNI.printString("Hello from c!!");
 }
 
}




# g++ -I/$JAVA_HOME/include -I$JAVA_HOME/include/linux -c hellojnimap.cpp
# g++ -shared -o hellojnimap.so hellojnimap.o
# java 메인클래스실행

Posted by 김용환 '김용환'

댓글을 달아 주세요

  1. Favicon of http://www.hobbit.co.kr BlogIcon hobbit.co.kr 2011.12.16 13:39  댓글주소  수정/삭제  댓글쓰기

    참고하고갑니다 ^^ 감사합니다