この記事では、Java ローカル インターフェイス JNI の使用方法に関する関連情報を詳しく紹介します。この記事を通じて、JNI プログラミングを徹底的に使いこなせるようになることを願っています。
Java ローカルの使用方法を詳しく説明します。インターフェース JNI
Java プログラムの場合 従業員にとって Java 言語のメリットや利点については説明する必要はないと思います。誰もが自然に多くのことを教えてくれます。しかし、私たち Java プログラマは、Java 言語にも独自の欠点があることを認めなければなりません。たとえば、パフォーマンスや基礎となる層の処理の点で欠点があります。したがって、Java はいくつかのローカル インターフェイスを提供します。その主な機能は、Java プログラムが仮想マシンを介してネイティブ コードと対話するための標準的な方法を提供することです。これは、通常、 Java ネイティブ インターフェイス (JNI - Java ネイティブ インターフェイス) と呼ばれます。これにより、Java 仮想マシン (VM) 内で実行される Java コードが、C、C++、アセンブリ言語などの他のプログラミング言語で書かれたアプリケーションやライブラリと相互運用できるようになります。 JNI の最も重要な利点は、基盤となる Java 仮想マシンの実装に制限が課されないことです。したがって、Java 仮想マシンのベンダーは、仮想マシンの他の部分に影響を与えることなく、JNI のサポートを追加できます。プログラマは、JNI 対応のすべての Java 仮想マシンで動作するネイティブ アプリケーションまたはライブラリの 1 つのバージョンを作成するだけで済みます。ネイティブ コードを操作する必要がある理由を見てみましょう:
1: アプリケーションのパフォーマンスを向上させる。 Java は C/C++ やアセンブリ言語に比べて比較的「高度」であることがわかっています。実際、ここでの先進的な点は、プログラマーの作業を簡素化することです。低レベルの処理の多くは Java 仮想マシンによって実行されます。しかし結局のところ、Java は最下位層に直接アクセスする場合と比較して、仮想マシン プロセスのステップが 1 つ多いため、パフォーマンスはこれらのネイティブ言語よりわずかに遅くなります。
2: 基礎となる関連関数を実装します。 Java プラットフォームが提供する標準クラス ライブラリと強力な API で、ほとんどの機能を実行できます。ただし、基礎となるハードウェアを処理する一部の関数は、Java API によって提供されるクラス ライブラリでは完了できません。
3: ネイティブ コードを使用して記述された既存のプログラムと統合します。 C や C++ などのネイティブ言語で書かれたソフトウェアをオペレーティング システムに統合する場合、JNI を使用できます。
JNI インターフェース関数とポインター
プラットフォーム関連のコードは、JNI 関数を呼び出すことによって Java 仮想マシン関数にアクセスします。 JNI 関数は、インターフェイス ポインターを通じて使用できます。インターフェイス ポインターは、ポインターの配列を指すポインターへのポインターであり、ポインターの配列内の各要素はインターフェイス関数を指します。各インターフェイス関数は、配列内の所定のオフセットに配置されます。次の図は、インターフェイス ポインターの構成を示しています。接続 JNI インターフェイスの構造は、C++ 仮想関数テーブルまたは COM インターフェイスに似ています。ハードコーディングされた関数テーブルの代わりにインターフェイス テーブルを使用する利点は、JNI 名前空間をプラットフォーム固有のコードから分離しておけることです。仮想マシンは、複数のバージョンの JNI 関数テーブルを簡単に提供できます。たとえば、仮想マシンは次の 2 つの JNI 関数テーブルをサポートできます。
· 1 つのテーブルは、プログラムのデバッグに適した、不正なパラメータの包括的なチェックを実行します。
· 他のテーブルは、必要な最小限のチェックのみを実行します。 JNI仕様のため、効率が高くなります。
JNI インターフェイス ポインタは現在のスレッドでのみ有効です。したがって、ネイティブ メソッドは、あるスレッドから別のスレッドにインターフェイス ポインタを渡すことができません。 JNI を実装する仮想マシンは、JNI インターフェイス ポインタが指す領域にローカル スレッド データを割り当てて保存できます。
ネイティブ メソッドは、JNI インターフェイス ポインターをパラメーターとして受け入れます。
//Compute.java public class Compute{ public native double sqrt(double params); static{ //调用动态链接库 System.loadLibrary(“compute”); }(2) バイトコードにコンパイルされる
このプロセスでは、ネイティブキーワード宣言が使用されているため、Java コンパイラはコード本体のない JNI メソッド部分を無視します。
(3) 関連する JNI メソッドのヘッダー ファイルを生成します 这个过程的实现一般是通过利用jlavah-jni * class生成的(-jni可以省略),也可以手工生成该文件;但是由于 Java 虚拟机是根据一定的命名规范完成对JNI方法的调用,所以手工编写头文件需要特别小心。
上述文件产生的头文件部分代码如下:
//Compute.h extern“C”{ JNIEXPORT jdoubleJNICALL Java_Compute_comp(JNI-Env *, jobject, jdoubleArray);
JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。
JNI函数的参数也由三部分组成:首先是JNIEnv *,是一个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同一一非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其Java类的引用;其余的参数对应通常Java方法的参数,参数类型需要根据一定规则进行映射。
(4)编写相应方法的实现代码
在编码过程中,需要注意变量的长度问题,例如Java的整型变量长度为32位,而C语言为16位,所以要仔细核对变量类型映射表,防止在传值过程中出现问题。
(5)将JNI实现代码编译成动态链接库
编译过程是利用C/C++编译器实现的,在windows平台上,编译和连接的结果是动态链接库DLL文件。当要使用生成的动态链接库时,调用者类中需要显式调用该链接库dll文件。
经过上述处理,基本上完成了一个包含本地化方法的Java类的开发。
附录:将Jav类型映射到本地 C 类型
基本类型和本地等效类型 | ||
Java 类型 |
本地类型 |
说明 |
boolean |
jboolean |
无符号,8 位 |
byte |
jbyte |
无符号,8 位 |
char |
jchar |
无符号,16 位 |
short |
jshort |
有符号,16 位 |
int |
jint |
有符号,32 位 |
long |
jlong |
有符号,64 位 |
float |
jfloat |
32 位 |
double |
jdouble |
64 位 |
void |
void |
N/A |
为了使用方便,特提供以下定义。
#define JNI_FALSE 0 #define JNI_TRUE 1
jsize 整数类型用于描述主要指数和大小:
typedef jint jsize;
故障排除
当使用 JNI 从 Java 程序访问本机代码时,您会遇到许多问题。您会遇到的三个最常见的错误是:
1)无法找到动态链接。它所产生的错误消息是:java.lang.UnsatisfiedLinkError。这通常指无法找到共享库,或者无法找到共享库内特定的本机方法。
2)无法找到共享库文件。当用 System.loadLibrary(String libname) 方法(参数是文件名)装入库文件时,请确保文件名拼写正确以及没有指定扩展名。还有,确保库文件的位置在类路径中,从而确保 JVM 可以访问该库文件。
3)无法找到具有指定说明的方法。确保您的 C/C++ 函数实现拥有与头文件中的函数说明相同的说明。
结束语
从 Java 调用 C 或 C++ 本机代码(虽然不简单)是 Java 平台中一种良好集成的功能。虽然 JNI 支持 C 和 C++,但 C++ 接口更清晰一些并且通常比 C 接口更可取。正如您已经看到的,调用 C 或 C++ 本机代码需要赋予函数特殊的名称,并创建共享库文件。当利用现有代码库时,更改代码通常是不可取的。要避免这一点,在 C++ 中,通常创建代理代码或代理类,它们有专门的 JNI 所需的命名函数。然后,这些函数可以调用底层库函数,这些库函数的说明和实现保持不变。
以上がJavaローカルインターフェースJNIで使用されるメソッドの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。