たまに必要になるJNI。ノート替わりにメモしておく。
例えばこんな感じのSample.javaコードを用意して、ここからヘッダファイルを生成する。
public class Sample {
public static native int readValue();
}
javac Sample.java -h .
javacのヘルプみればわかるけど、-h
の後ろにはヘッダファイルを生成するディレクトリを指定する。
生成されたヘッダファイルSample.hは以下。
#include <jni.h>
#ifndef _Included_Sample
#define _Included_Sample
#ifdef __cplusplus
extern "C" {
#endif
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が呼び出せるようになった。
ちなみにWindowsの場合は.soが.dllになったりするのでコンパイルオプションなど取り扱いが変わる。