おそらくすでにご想像のとおり、JNIについて説明します。 これが何であるかを知らない人のために、私は説明します:JNI(またはJavaネイティブインターフェイス)は、Javaマシンからネイティブコード呼び出しを行うことを可能にするものです。
なぜこれが必要なのでしょうか? いくつかの理由があります:ネイティブプラットフォーム用に既に記述されたコードを使用する必要性、単一のJVMで実行できないものを実装する必要性(たとえば、特定の鉄片での作業)、および重要なコード部分の実行の高速化(ただし、これは非常に物議を醸す点です)。
JNIの仕組み
c ++で記述され、動的にリンクされたライブラリ(たとえば、Windowsではdllになります)にあるメソッドを呼び出す必要のある種類のjavaクラスがあるとします。 これのために何をすべきですか?まず、あるクラスのメソッドをネイティブとして宣言します。 これは、JVMがこのメソッドを呼び出すときに、制御をネイティブコードに転送することを意味します。
次に、ネイティブライブラリをロードする必要があります。 これを行うには、パラメーターとしてライブラリー名を取る
System.loadLibrary(String)
呼び出すことができます。 この呼び出しの後、ライブラリはJVMアドレス空間にロードされます。
ここで、次のJavaクラスがあるとします。
パッケージ my.mega.pack ;ここでは、便宜上、クラスの静的領域に
パブリック クラス NativeCallsClass
{
静的
{
システム loadLibrary ( "megalib" ) ;
}
ネイティブ public static void printOne ( ) ;
ネイティブ public static void printTwo ( ) ;
}
loadLibrary()
を配置します。
NativeCallsClass.printOne()
を呼び出すと仮定します。 次に、JVMは、ライブラリで
Java_my_mega_pack_NativeCallsClass_printOne(...)
という名前のメソッドを探します。
C ++でのJNI関数の宣言
ネイティブとしてマークされたメソッドを持つクラスをjavaで作成しました。 ここで、呼び出すC ++関数の宣言を使用してヘッダーを作成する必要があります。もちろん、手動で作成することもできます。 しかし、もっと便利な方法があります:
javac -d bin / src / my / mega / pack / NativeCallsClass.java cd bin javah my.mega.pack.NativeCallsClass
クラスをコンパイルしてから、javahユーティリティを使用します。 その後、my_mega_pack_NativeCallsClass.hというファイルが作成されます。 これがヘッダーです。 次のようになります。
/ *このファイルは編集しないでください-マシンで生成されます* /ここで最も重要なことは、
#include <jni.h>
/ *クラスmy_mega_pack_NativeCallsClassのヘッダー* /
#ifndef _Included_my_mega_pack_NativeCallsClass
#define _Included_my_mega_pack_NativeCallsClass
#ifdef __cplusplus
extern "C" {
#endif
/ *
*クラス:my_mega_pack_NativeCallsClass
*メソッド:printOne
*署名:()V
* /
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printOne
( JNIEnv * 、jclass ) ;
/ *
*クラス:my_mega_pack_NativeCallsClass
*メソッド:printTwo
*署名:()V
* /
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printTwo
( JNIEnv * 、jclass ) ;
#ifdef __cplusplus
}
#endif
#endif
Java_my_mega_pack_NativeCallsClass_printOne(JNIEnv *env, jclass myclass)
と
Java_my_mega_pack_NativeCallsClass_printTwo(JNIEnv *env, jclass myclass)
Java_my_mega_pack_NativeCallsClass_printOne(JNIEnv *env, jclass myclass)
の2つの関数のシグネチャです。
それらを実現する必要があります。 まず、署名を扱います。 envは、仮想マシンへのインターフェースです。 JVMを使用したすべての操作は、JVMを使用して実行されます。 後でこれをより詳細に分析します。 myclassは、この関数で識別されるネイティブメソッドを持つJavaクラスの識別子
NativeCallsClass
。
NativeCallsClass
、この例では
NativeCallsClass
です。 メソッドが静的として宣言されている場合、jclassは2番目のパラメーターとして渡されることに注意してください。 通常のメソッドの場合、メソッドを呼び出したオブジェクトを識別するジョブジェクトが与えられます(実際、これはこれに類似しています)。
これらの機能のみを実装できます。
#include <iostream>
#include "my_mega_pack_NativeCallsClass.h"
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printOne ( JNIEnv * env、jclass myclass )
{
std :: cout << "One" << std :: endl ;
}
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_printTwo ( JNIEnv * env、jclass myclass )
{
std :: cout << "Two" << std :: endl ;
}
データをネイティブコードに、またはその逆に転送します
もっと複雑な動作を実装しましょう。 inputIntとoutputIntの2つのメソッドがあります。 それらの1つはコンソールから番号を読み取り、2つ目は出力します。 javaクラスは次のようになります。パッケージ my.mega.pack ;javahを起動すると、メソッドシグネチャが少し変更されていることがわかります。 現在は次のとおりです。
パブリック クラス NativeCallsClass
{
静的
{
システム loadLibrary ( "megalib" ) ;
}
ネイティブ public static int inputInt ( ) ;
ネイティブ パブリック 静的 void outputInt ( int v ) ;
}
JNICALL Java_my_mega_pack_NativeCallsClass_inputInt ( JNIEnv * 、jclass ) ;jintはtypedefです。 実際、Javaのintに対応するプリミティブ型(たとえば、int)を示します。 ご覧のとおり、タスクは以前のものよりもそれほど複雑ではありませんでした:)私たちの機能は次のようになります。
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_outputInt ( JNIEnv * 、jclass、jint ) ;
#include <iostream>
#include "my_mega_pack_NativeCallsClass.h"
JNIEXPORT jint JNICALL Java_my_mega_pack_NativeCallsClass_inputInt ( JNIEnv * env、jclass myclass )
{
int ret;
std :: cin >> ret;
return ret;
}
JNIEXPORT void JNICALL Java_my_mega_pack_NativeCallsClass_outputInt ( JNIEnv * env、jclass myclass、jint v )
{
std :: cout << v << std :: endl ;
}
まとめると
そのため、第1部では、JNIの仕組み、Javaクラスの作成方法、およびJNIを介して呼び出されるC ++関数を作成するタイミングと方法をネイティブ呼び出しできる方法について説明しました。 次のパートでは、C ++コードからJVMと対話し、クラス、オブジェクト、フィールド、メソッドを操作し、C ++クラスを表すプロキシJavaクラスを作成し、C ++コードからJVMを実行することを検討します。当然、継続は誰かにとって興味深い場合のみです:)