혼자 잡담) 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 메인클래스실행