수색…


매개 변수

매개 변수 세부
JNIEnv JNI 환경의 포인터
작업 static 가 아닌 native 메소드를 호출 한 객체
jclass static native 메소드를 호출 한 클래스

비고

JNI를 설정하려면 Java 컴파일러와 원시 컴파일러가 모두 필요합니다. IDE와 OS에 따라 몇 가지 설정이 필요합니다. Eclipse에 대한 안내서는 여기 에서 찾을 수 있습니다 . 전체 자습서는 여기 에서 찾을 수 있습니다 .

다음은 Windows에서 Java-C ++ 링키지를 설정하는 단계입니다.

  • javac 사용하여 Java 소스 파일 ( .java )을 클래스 ( .class )로 javac .
  • javah 사용하는 native 메소드가 들어있는 Java 클래스에서 헤더 ( .h ) 파일을 javah . 이러한 파일은 구현할 책임이있는 메소드를 네이티브 코드에 "지시"합니다.
  • native 메소드를 구현하는 C ++ 소스 파일 ( .cpp )에 헤더 파일 ( #include )을 포함시킵니다.
  • C ++ 소스 파일을 컴파일하고 라이브러리 ( .dll )를 만듭니다. 이 라이브러리에는 원시 코드 구현이 포함되어 있습니다.
  • 라이브러리 경로 ( -Djava.library.path )를 지정하고 Java 소스 파일 ( System.loadLibrary(...) )에로드하십시오.

콜백 (원시 코드에서 Java 메소드 호출)은 메소드 설명자를 지정해야합니다. 설명자가 올바르지 않으면 런타임 오류가 발생합니다. 이 때문에 javap -s 를 사용하여 설명자를 작성하는 것이 도움이됩니다.

자바에서 C ++ 메소드 호출하기

Java의 정적 및 구성원 메소드는 해당 구현이 공유 라이브러리 파일에서 발견 될 것임을 나타 내기 위해 원시 로 표시 될 수 있습니다. 네이티브 메소드를 실행하면 JVM은로드 된 라이브러리 ( 네이티브 라이브러리로드 참조)에서 해당 함수를 찾고 단순한 이름 변환 체계를 사용하여 인수 변환 및 스택 설정을 수행 한 다음 제어를 원시 코드로 넘깁니다.

Java 코드

/*** com/example/jni/JNIJava.java **/

package com.example.jni;

public class JNIJava {
    static {
        System.loadLibrary("libJNI_CPP");
    }

    // Obviously, native methods may not have a body defined in Java
    public native void printString(String name);
    public static native double average(int[] nums);

    public static void main(final String[] args) {
        JNIJava jniJava = new JNIJava();
        jniJava.printString("Invoked C++ 'printString' from Java");

        double d = average(new int[]{1, 2, 3, 4, 7});
        System.out.println("Got result from C++ 'average': " + d);
    }
}

C ++ 코드

원시 함수 선언을 포함하는 헤더 파일은 대상 클래스의 javah 도구를 사용하여 생성해야합니다. 빌드 디렉토리에서 다음 명령을 실행하십시오.

javah -o com_example_jni_JNIJava.hpp com.example.jni.JNIJava

... 다음 헤더 파일을 생성합니다 ( 간결하게 주석을 제거했습니다 ).

// com_example_jni_JNIJava.hpp

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> // The JNI API declarations

#ifndef _Included_com_example_jni_JNIJava
#define _Included_com_example_jni_JNIJava
#ifdef __cplusplus
extern "C" { // This is absolutely required if using a C++ compiler
#endif

JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString
  (JNIEnv *, jobject, jstring);

JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average
  (JNIEnv *, jclass, jintArray);

#ifdef __cplusplus
}
#endif
#endif

다음은 구현 예입니다.

// com_example_jni_JNIJava.cpp

#include <iostream>
#include "com_example_jni_JNIJava.hpp"

using namespace std;

JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString(JNIEnv *env, jobject jthis, jstring string) {
    const char *stringInC = env->GetStringUTFChars(string, NULL);
    if (NULL == stringInC)
        return;
    cout << stringInC << endl;
    env->ReleaseStringUTFChars(string, stringInC);
}

JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average(JNIEnv *env, jclass jthis, jintArray intArray) {
    jint *intArrayInC = env->GetIntArrayElements(intArray, NULL);
    if (NULL == intArrayInC)
        return -1;
    jsize length = env->GetArrayLength(intArray);
    int sum = 0;
    for (int i = 0; i < length; i++) {
        sum += intArrayInC[i];
    }
    env->ReleaseIntArrayElements(intArray, intArrayInC, 0);
    return (double) sum / length;
}

산출

위의 예제 클래스를 실행하면 다음과 같은 결과가 출력됩니다.

Java에서 C ++ 'printString'호출
C ++에서 얻은 결과 'average': 3.4

C ++ (콜백)에서 Java 메소드 호출

원시 코드에서 Java 메소드 호출은 두 단계 프로세스입니다.

  1. 메서드 명과 기술자를 사용해, GetMethodID JNI 함수를 가지는 메서드 포인터를 취득한다.
  2. 여기에 나열된 Call*Method 함수 중 하나를 Call*Method 하십시오 .

Java 코드

/*** com.example.jni.JNIJavaCallback.java ***/

package com.example.jni;

public class JNIJavaCallback {
    static {
        System.loadLibrary("libJNI_CPP");
    }
    
    public static void main(String[] args) {
        new JNIJavaCallback().callback();
    }

    public native void callback(); 

    public static void printNum(int i) {
        System.out.println("Got int from C++: " + i);
    }
    
    public void printFloat(float i) {
        System.out.println("Got float from C++: " + i);
    }
}

C ++ 코드

// com_example_jni_JNICppCallback.cpp

#include <iostream>
#include "com_example_jni_JNIJavaCallback.h"

using namespace std;

JNIEXPORT void JNICALL Java_com_example_jni_JNIJavaCallback_callback(JNIEnv *env, jobject jthis) {
    jclass thisClass = env->GetObjectClass(jthis);

    jmethodID printFloat = env->GetMethodID(thisClass, "printFloat", "(F)V");
    if (NULL == printFloat)
        return;
    env->CallVoidMethod(jthis, printFloat, 5.221);

    jmethodID staticPrintInt = env->GetStaticMethodID(thisClass, "printNum", "(I)V");
    if (NULL == staticPrintInt)
        return;
    env->CallVoidMethod(jthis, staticPrintInt, 17);
}

산출

C ++에서 부동 소수점 가져 오기 : 5.221
C ++에서 가져온 int : 17

설명자 가져 오기

설명자 (또는 내부 형식 서명 )는 컴파일 된 .class 파일의 javap 프로그램을 사용하여 가져옵니다. 다음은 javap -p -s com.example.jni.JNIJavaCallback 의 결과입니다.

Compiled from "JNIJavaCallback.java"
public class com.example.jni.JNIJavaCallback {
  static {};
    descriptor: ()V

  public com.example.jni.JNIJavaCallback();
    descriptor: ()V

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V

  public native void callback();
    descriptor: ()V

  public static void printNum(int);
    descriptor: (I)V  // <---- Needed

  public void printFloat(float);
    descriptor: (F)V  // <---- Needed
}

네이티브 라이브러리로드 중

Java에서 공유 라이브러리 파일을로드하는 일반적인 관용구는 다음과 같습니다.

public class ClassWithNativeMethods {
    static {
        System.loadLibrary("Example");
    }

    public native void someNativeMethod(String arg);
    ...

System.loadLibrary 대한 호출은 거의 항상 정적이므로 클래스로드 중에 발생하므로 공유 라이브러리가로드되기 전에 원시 메소드를 실행할 수 없습니다. 그러나 다음이 가능합니다.

public class ClassWithNativeMethods {
    // Call this before using any native method
    public static void prepareNativeMethods() {
        System.loadLibrary("Example");
    }

    ...

이렇게하면 필요 할 때까지 공유 라이브러리 로딩을 지연 할 수 있지만 java.lang.UnsatisfiedLinkError 를 피하기 위해 추가로주의해야합니다.

대상 파일 조회

공유 라이브러리 파일은 런타임시 -Djava.library.path= JVM 인수를 사용하여 재정의 할 수있는 java.library.path 시스템 특성으로 정의 된 경로에서 검색됩니다.

java -Djava.library.path=path/to/lib/:path/to/other/lib MainClassWithNativeMethods

시스템 경로 구분 기호에주의하십시오 (예 : Windows 사용 ; 대신 : .

System.loadLibrary 는 플랫폼에 따라 라이브러리 파일 이름을 확인합니다. 위의 코드는 Linux의 libExample.so 파일과 Windows의 Example.dll 파일을 필요로합니다.

System.loadLibrary 의 대안은 System.load(String) .이 라이브러리는 java.library.path 조회를 우회하여 공유 라이브러리 파일의 전체 경로를 사용합니다.

public class ClassWithNativeMethods {
    static {
        System.load("/path/to/lib/libExample.so");
    }

    ...


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow