JNI:JavaとC ++で友達になろう

はじめに



Javaでは、通常のJavaクラスの外部でいくつかのアクションが実行される場合があります。 たとえば、C / C ++または他の言語で記述されたコードを実行する必要があります。



この記事では、この問題を実用的な観点から検討します。つまり、 JNIを使用してJavaコードとC ++コードの相互作用の簡単な例を作成します。 この記事には超自然的なものは含まれていませんが、それを扱ったことのない人へのリマインダーです。



ここでは、 System.load()



メソッドによって呼び出されるネイティブライブラリを動的にロードする可能性があります。詳細については、 こちらをご覧ください



問題の声明



「Hello world」画面を表示するネイティブメソッドを含むクラスを実装する必要があります。



JNIHelloWorld.java



 package ru.forwolk.test; public class JNIHelloWorld { native void printHelloWorld(); }
      
      





ヘッダー生成



C ++用にこのクラスのヘッダーを生成します。



まず、プロジェクトのルートにフォルダーを作成します。ここで、バイナリを収集します。



 mkdir bin
      
      





次に、クラスをこのディレクトリにコンパイルします



 javac -d bin/ src/ru/forwolk/test/JNIHelloWorld.java
      
      





binフォルダーには、クラスファイルがあります。 むしろ、bin / ru / forwolk / test /にあります。 binフォルダーに移動して、ヘッダーを生成します。



 cd bin/ javah ru.forwolk.test.JNIHelloWorld
      
      





ご覧のとおり、ru_forwolk_test_JNIHelloWorld.hファイルがbinフォルダーに表示されています。 簡単にするために、名前をJNIHelloWorld.hに変更します



 mv ru_forwolk_test_JNIHelloWorld.h JNIHelloWorld.h
      
      





開くと、次の図が表示されます。



 JNIHelloWorld.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class ru_forwolk_test_JNIHelloWorld */ #ifndef _Included_ru_forwolk_test_JNIHelloWorld #define _Included_ru_forwolk_test_JNIHelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: ru_forwolk_test_JNIHelloWorld * Method: printHelloWorld * Signature: ()V */ JNIEXPORT void JNICALL Java_ru_forwolk_test_JNIHelloWorld_printHelloWorld (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
      
      





C ++実装



ソースコードJNIHelloWorld.cppを使用してファイルを作成します。 この目的のために、必要なファイルを挿入するプロジェクトをClionで作成しました。 メソッドを実装します。



 JNIHelloWorld.cpp #include <iostream> #include "JNIHelloWorld.h" JNIEXPORT void JNICALL Java_ru_forwolk_test_JNIHelloWorld_printHelloWorld (JNIEnv *, jobject) { std::cout << "Hello world!"; }
      
      





Clionが正常に機能するためには、CMakeLists.txtファイルにJavaライブラリを追加する必要があります。



 //  $JAVA_HOME --   Java include_directories($JAVA_HOME/include) include_directories($JAVA_HOME/include/linux) link_directories($JAVA_HOME/include) link_directories($JAVA_HOME/include/linux)
      
      





次にコンパイルする



 g++ -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -fPIC JNIHelloWorld.cpp -shared -o helloworld.so -Wl,-soname -Wl,--no-whole-archive
      
      





Javaでダウンロードする



helloworld.soファイルは、プロジェクトのルートフォルダーに表示されました。 Javaプロジェクトのbin /フォルダーに移動します。



次に、ライブラリをダウンロードする必要があります。 クラスに直接ライブラリを静的にロードすることをお勧めします。 ダウンロードをJNIHelloWorldクラスに直接追加します



 static { // $PROJECT_ROOT --     System.load("$PROJECT_ROOT/bin/helloworld.so"); }
      
      





これで、このクラスを完全に使用できます。 見てみましょう。



 public static void main(String[] args) { JNIHelloWorld p = new JNIHelloWorld(); p.printHelloWorld(); }
      
      





出力では



 Hello world! Process finished with exit source 0
      
      





パラメータを渡す



しかし、コードを実行するだけでなく、パラメーターを渡して回答を受け取る必要がある場合はどうでしょうか。 2つの数値を乗算する別の方法を検討してください。 JNIHelloWorldクラスにメソッドを追加します



 native int multiply(int a, int b);
      
      





ヘッダーを生成するために上記で説明したのと同じアクションを実行します。 ご覧のとおり、以下が生成されました



 /* * Class: ru_forwolk_test_JNIHelloWorld * Method: multiply * Signature: (II)I */ JNIEXPORT jint JNICALL Java_ru_forwolk_test_JNIHelloWorld_multiply (JNIEnv *, jobject, jint, jint);
      
      





JNIHelloWorld.cppにメソッドを実装します



 JNIEXPORT jint JNICALL Java_ru_forwolk_test_JNIHelloWorld_multiply (JNIEnv *, jobject, jint a, jint b) { return a * b; }
      
      





ここでも、上記の手順を実行してライブラリをプルアップし、mainに行を追加して2つの数値の積の結果を出力し、実行します



 public static void main(String[] args) { JNIHelloWorld p = new JNIHelloWorld(); System.out.println(p.multiply(2, 2)); p.printHelloWorld(); }
      
      





コンソールで何を取得しますか



 4 Hello world! Process finished with exit source 0
      
      





おわりに



C / C ++で記述されたコードを使用して、Javaの可能性を調査しました。 これは、さまざまな目的で使用できます。たとえば、コードの実行速度を上げる、直接干渉からコードを保護する、その他の目的に使用できます。 この記事がJNIの基本を理解するのに役立つことを本当に願っています。



私がパブリックドメインに投稿したすべてのコード。 cppディレクトリに、余分なClionプロジェクトファイルなしでC ++クラスを配置しました。



さらに読む



また、このトピックに関するより広い観点から、次の記事に注意することをお勧めします。

JNI、ネイティブライブラリのロード。 その場でjava.library.pathを変更する

ネイティブメソッドは高価ですか? JNI Secret Extension



All Articles