Java/JNI/ネイティブコードの実行手順 の変更点


#author("2020-04-25T05:52:40+00:00","default:haikikyou","haikikyou")
#author("2020-04-25T05:53:45+00:00","default:haikikyou","haikikyou")
[[Java/JNI]]
#contents

* JNI [#s2524a7e]

JNIは、'''Java Native Interface'''の略であり、JavaとC/C++のネイティブコード間の連携を可能にするインタフェース仕様と技術である。~
既存のネイティブで実行するプログラム資産やJavaで対応できないプラットフォーム固有の機能を利用するには、JNIやJava Native Access (JNA) がある。

* ネイティブコード実行の手順 [#j0729c3e]

JavaからCのプログラムを呼び出す流れについて見ていこう。

+ ネイティブコードのインタフェースを定義
+ Javaコードの作成
+ Javaコードをjavacでコンパイル
+ javahでヘッダファイルを生成
+ ネイティブコードをコンパイルし共有ライブラリを作成

サンプルで手順を見ていく。~
サンプルで使用するディレクトリ構造は以下の通り。

#geshi{{{
$ tree .
.
├── Makefile
├── hello.c
└── src
    └── main
        └── java
            └── sample
                └── JNISample.java

4 directories, 3 files
}}}

- パッケージsampleにJavaプログラムを配置する。
- hello.cにネイティブコードを書き、JNISample.javaからhello.cの関数を呼び出す。

Makefileはビルドと実行の簡単化のために作成する。


** ネイティブコードのインタフェースを定義 [#g6fb7430]

今回のサンプルで使用するネイティブコードは以下。

#geshi(c){{{
#include "JNISample.h"

JNIEXPORT void JNICALL
Java_sample_JNISample_hello (JNIEnv *env, jobject obj, jstring jmsg)
{
  jboolean isCopy;
  const char *msg = (*env)->GetStringUTFChars(env, jmsg, &isCopy);

  if (msg == NULL)
    return;

  printf("Hello, %s\n", msg);

  (*env)->ReleaseStringUTFChars(env, jmsg, msg);
}
}}}

** Javaコードの作成 [#d5bef258]

Javaプログラムでは、ネイティブコード呼び出しのメソッドは&code(){native};キーワードで指定する。~
JNISampleに引数を与えて実行すると、標準出力にメッセージを表示するだけの簡単なサンプルとする。

#geshi(java){{{
package sample;

class JNISample {
    static {
        System.loadLibrary("jnisample");
    }
    // C側のプログラムを呼び出すインターフェース
    public native void hello(String message);

    public static void main(String[] args) {
        JNISample hello = new JNISample();
        hello.hello(args[1]);
    }
}
}}}

** Javaコードをjavacでコンパイル [#d7f6736b]

作成したJavaプログラムをコンパイルする。通常通りの&code(){javac};コマンドによるコンパイルである。~
以下では、classesの下にclassファイルを出力している。

#geshi{{{
mkdir -p classes
javac -d classes src/main/java/sample/JNISample.java
}}}

** javahでヘッダファイルを生成 [#x96bdfbc]

&code(){javah};コマンドで、ネイティブコードでインクルードするヘッダファイルを生成する。~
このヘッダファイルは、&code(){jni.h};のヘッダファイルをインクルードしているので、hello.c内でJNIインタフェースを使用できる。

#geshi(java){{{
javah -o JNISample.h -classpath classes sample.JNISample
}}}

デフォルトだと、&code(){sample_JNISample.h};のようなヘッダファイル名となる。~
パッケージ名が長いとファイル名が長くなってしまうこと、ファイル名を固定化したい、の理由で上記では&code(){-o};オプションで生成されるファイル名を指定する。

生成されたヘッダファイルは以下のようになる。~
このファイルは自動生成されるため基本的には修正しない。

#geshi(c){{{
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class sample_JNISample */

#ifndef _Included_sample_JNISample
#define _Included_sample_JNISample
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     sample_JNISample
 * Method:    hello
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_sample_JNISample_hello
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
}}}

JNIで自動生成されるメソッドの引数には、JNIEnvとjobject(staticの場合は、jclass)が渡される。~
jobjectはオブジェクト、jclassはクラスの参照である。~
その後にメソッドの引数定義が続く。


** ネイティブコードをコンパイルし共有ライブラリを作成 [#f0e9831e]

ネイティブコードをビルドして、共有ライブラリを作成する。~
以下はmacOS上でビルドしている。各プラットフォームの仕様を参照されたい。

#geshi(bash){{{
gcc -O2 -Wall -I$(/usr/libexec/java_home)/include{,/darwin} -shared -o libjnisample.jnilib hello.c
}}}
&label(info){補足}; &code(){{,darwin}};は、Bashのブレース展開

以上でライブラリを作成できたので実際に実行してみる。

#geshi(bash){{{
$ java -classpath classes sample.JNISample tarou
Hello, tarou
}}}

メッセージを表示することができている。

&label(sample){サンプル}; ''Makefileの例''

Makefile及びサンプルプログラムを添付しておく。~
macで動作することを想定している。

#geshi(make){{{
JAVA_HOME   := $(shell /usr/libexec/java_home)
CC           = gcc
CFLAGS       = -O2 -Wall $(addprefix -I$(JAVA_HOME)/,include include/darwin)
SOURCES      = hello.c
OBJS         = $(SOURCES:%.c=%.o)
PROGRAM      = libjnisample.jnilib
CLASSES      = src/main/java/sample/JNISample.java

.SUFFIXES: .java .class

.PHONY: all
all: $(PROGRAM)

$(PROGRAM): JNISample.h $(OBJS)
	$(CC) $(CFLAGS) -shared $(OBJS) -o $@

JNISample.h: classes
	javah -o JNISample.h -classpath classes sample.JNISample

.PHONY: classes
classes: $(CLASSES:.java=.class)

.java.class:
	mkdir -p classes
	javac -d classes $<

.PHONY: run
run:
	java -classpath classes -Djava.library.path=. sample.JNISample $(ARGS)

.PHONY: clean
clean:
	$(RM) -rf *.o *.jnilib classes JNISample.h
}}}

ビルドと実行方法は以下の通りである。

#geshi{{{
$ make
$ make ARGS="tarou" run
}}}

&label(sample){サンプル}; ''JNIサンプルプログラム''
&ref(jnisample.tar.gz);

* 参考リンク [#g542d2d3]

- [[Java Native Interface仕様の目次>https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/jniTOC.html]] - &size(11){&color(gray){on https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/jniTOC.html};};
- [[JNI関数>https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/functions.html]] - &size(11){&color(gray){on https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/functions.html};};
- [[NI:Java Native Interfaceプログラミング―C/C++コードを用いたJavaアプリケーション開発 (Java books)>https://www.amazon.co.jp/dp/4894710803]] -- &size(11){&color(gray){on https://www.amazon.co.jp/dp/4894710803};};
- [[JNI Example (Mac OS)>https://gist.github.com/DmitrySoshnikov/8b1599a5197b5469c8cc07025f600fdb]] - &size(11){&color(gray){on https://gist.github.com/DmitrySoshnikov/8b1599a5197b5469c8cc07025f600fdb};};
- [[eginning Java 8 APIs, Extensions and Libraries: Swing, JavaFX, JavaScript, JDBC and Network Programming APIs (Expert's Voice in Java)>https://www.amazon.co.jp/dp/1430266619/]] - &size(11){&color(gray){on https://www.amazon.co.jp/dp/1430266619/};};
- AdoptOpenJDK/openjdk-jdk8u
-- [[hotspot/src/share/vm/prims/jni.h>https://github.com/AdoptOpenJDK/openjdk-jdk8u/blob/master/hotspot/src/share/vm/prims/jni.h]] - &size(11){&color(gray){on https://github.com/AdoptOpenJDK/openjdk-jdk8u/blob/master/hotspot/src/share/vm/prims/jni.h};};

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
TOP | 閉じる | ダブルクリックで閉じる