たまに必要になるJNI。ノート替わりにメモしておく。
例えばこんな感じのSample.javaコードを用意して、ここからヘッダファイルを生成する。
public class Sample { public static native int readValue(); }
javac Sample.java -h .
javacのヘルプみればわかるけど、-h
の後ろにはヘッダファイルを生成するディレクトリを指定する。
生成されたヘッダファイルSample.hは以下。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Sample */ #ifndef _Included_Sample #define _Included_Sample #ifdef __cplusplus extern "C" { #endif /* * Class: Sample * Method: readValue * Signature: ()I */ JNIEXPORT jint JNICALL Java_Sample_readValue (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif
このヘッダファイルをincludeしたcのコードは以下。
#include "Sample.h" JNIEXPORT jint JNICALL Java_Sample_readValue (JNIEnv *env, jclass clazz) { return 13; }
ヘッダファイルでは引数の変数名が省略されているので、コピペする場合は引数名を決めること。
とりあえずgccでコンパイルする。(最近はAmazon Correttoをつかっているので。)
gcc -I/usr/lib/jvm/java-11-amazon-corretto/include -I/usr/lib/jvm/java-11-amazon-corretto/include/linux sample.c -shared -o libSample.so
このとき-shared
を指定して、-o
で共有オブジェクト.soとしてコンパイルする必要がある。(そりゃそうだ)
あとは、Javaコードからsoをロードする必要がある。とりあえずはstaticブロックに追加して再コンパイルする。
public class Sample { static { System.loadLibrary("Sample"); } public static native int readValue(); }
で、これは普通に呼び出せる。
public static class Main { public static void main(String[] args) { System.out.printf("readValue: %d\n", Sample.readValue()); } }
上記のコードをMain.javaとすると以下でコンパイルする。
javac Main.java
soをロードするにはjava.library.pathをパスを指定する必要があるので実行は以下。
java -Djava.library.path=. Main
実行結果は以下となる。
readValue: 13
これで最低限のJNIが呼び出せるようになった。