サーチ…


パラメーター

パラメータ詳細
JNIEnv JNI環境へのポインタ
仕事static nativeメソッドを呼び出したオブジェクト
jclass static nativeメソッドを呼び出したクラス

備考

JNIを設定するには、Javaコンパイラとネイティブコンパイラの両方が必要です。 IDEとOSに応じて、いくつかの設定が必要です。 Eclipseのためのガイドはここにあります 。完全なチュートリアルはここで見つけることができます

これらは、Windows上でJava-C ++リンケージを設定する手順です。

  • javacを使用してJavaソースファイル( .java )をクラス( .class )にコンパイルします。
  • javahを使用してnativeメソッドを含むJavaクラスからヘッダー( .h )ファイルを作成します。これらのファイルは、ネイティブコードをどのメソッドに実装するかを「指示」します。
  • nativeメソッドを実装するC ++ソースファイル( .cpp )にヘッダファイル( #include )をインクルードします。
  • C ++ソースファイルをコンパイルし、ライブラリ( .dll )を作成します。このライブラリにはネイティブコードの実装が含まれています。
  • ライブラリパス( -Djava.library.path )を指定し、Javaソースファイル( System.loadLibrary(...) )にロードします。

コールバック(ネイティブコードからJavaメソッドを呼び出す)では、メソッド記述子を指定する必要があります。記述子が間違っていると、実行時エラーが発生します。このため、 javap -s使って記述子を作成すると便利です。

Javaから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メソッドを呼び出すことは、2つのステップからなるプロセスです。

  1. メソッド名と記述子を使用して、 GetMethodID JNI関数を持つメソッドポインタを取得します。
  2. ここにリストされている Call*Method関数の1つを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へのSystem.loadLibraryは、ほとんどの場合、クラスのロード中に発生するように静的であり、共有ライブラリがロードされる前にネイティブメソッドが実行されないようにします。しかし、次のことが可能です:

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

    ...

これにより、必要になるまで共有ライブラリの読み込みを遅らせることができますが、 java.lang.UnsatisfiedLinkErrorを避けるためには特別な注意が必要です。

ターゲットファイルの検索

共有ライブラリファイルは、 java.library.pathシステムプロパティで定義されたパスで検索されます。実行時に-Djava.library.path= JVM引数を使用して上書きすることができます。

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

システムのパス区切りに注意してください;代わりに:

System.loadLibraryは、プラットフォーム依存の方法でライブラリファイル名を解決することに注意してください。上のコードスニペットは、LinuxではlibExample.so 、WindowsではExample.dllという名前のファイルがlibExample.soです。

System.loadLibraryの代わりに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