JNIより簡単にJavaとC/C++をつなぐ「JNA」とは
富士ソフト株式会社
高見 誠
2009/12/14
JNAを利用するための基本的な流れ
まず、Windows APIのprintfを呼び出すサンプルソースコードを見てみましょう。
リスト1 Windows APIのprintfを呼び出すJavaのサンプルソースコード 1 import com.sun.jna.Library;
2 import com.sun.jna.Native;
3
4 public class HelloWorld {
5 public interface CLibrary extends Library {
6 CLibrary INSTANCE = (CLibrary)
7 Native.loadLibrary("msvcrt" , CLibrary.class);
8
9 void printf(String format, Object... args);
10 }
11
12 public static void main(String[] args) {
13 CLibrary.INSTANCE.printf("Hello, World\n");
14 }
15 }
JNAでは、ネイティブライブラリをJavaのインターフェイスでマッピングします。5行目では、ネイティブライブラリにマッピングするインターフェイスCLibraryを宣言しています。インターフェイスCLibraryは、JNA提供のインターフェイスLibraryを継承します。
6・7行目では、ターゲットのネイティブライブラリをロードします。ここで、APIのprintfはWindowsのmsvcrt.dllに実装しているので、msvcrt.dllを指定しますが、自己開発のネイティブライブラリにアクセスする場合、そのネイティブライブラリ名を指定します。同時に、ネイティブライブラリにマッピングするインターフェイスも指定して、ネイティブライブラリとJavaインターフェイスとの関連を付けます。
ライブラリマッピングの指定後、9行目では、メソッドのマッピングで、呼び出すネイティブAPIのJavaメソッドを宣言します。Javaのメソッド名とシグネチャは、ネイティブAPIと一致しなければなりません。
13行目からは、普通のJavaメソッドを呼び出すように、ネイティブライブラリをアクセスしています。
■ C/C++コードを1行も書かず、ネイティブAPIを呼び出し!
ご覧のとおり、C/C++コードを1行も書かず、ネイティブAPIを呼び出せました。実は、プリミティブデータ型しか使わないAPIだけなら、JNA 3.2.0から、5行目のインターフェイスも書かず、もっと簡単に呼び出せます。詳細はJNAサイトを参照してください。
本文では、自定義データ型の呼び出し方法も説明する都合上、サンプルソースの方法で記述します。
■ 同様の機能をJNIで実現すると……
比較のために、Windows環境で同様の機能をJNIで実現する手順とソースコードを見てみましょう。
呼び出す側のJavaクラスファイルを作成します。この手順は、JNAと同じです。
JNIの場合(Java側) 1 public class HelloWorld {
2 public native void printf(String message);
3 static {
4 System.loadLibrary(“MyNativeLib”);
5 }
6
7 public static void main(String[] args) {
8 HelloWorld obj = new HelloWorld();
9 obj. printf(“Hello, World\n”);
10 }
11 }
作成したJavaクラスファイルをコンパイルします。専用コマンドで、JavaクラスファイルのCのヘッダーファイルを生成します。
javah -jni -classpath .\classes HelloWorld
以下のHelloWorld.hが生成されます。
HelloWorld.h/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: printf
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_HelloWorld_printf(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
Cプログラムで、引数のデータ変換、ネイティブAPIの呼び出し、戻り値のデータ変換を実装して、HelloWorld.cppファイルとして保存します。
HelloWorld.cpp 1 #include "HelloWorld.h"
2
3 JNIEXPORT void JNICALL Java_HelloWorld_printf(JNIEnv *env, jobject o, jstring s) {
4 const char *pMessage = env->GetStringUTFChars(s, 0);
5 ::printf(“%s”,pMessage);
6 env->ReleaseStringUTFChars(s, pMessage);
7 }
コンパイルして、Javaプログラムからアクセスする中間のネイティブライブラリMyNativeLib.dllを作成します。
決まりの手順でも難しくはありませんが、手間が掛かるのに加えて、データ変換のために、なじみのないCプログラムを実装しなければならないと考えると、JNAを利用するメリットは、一目瞭然でしょう。
JavaとC/C++のマッピング方法、6パターン
異なる言語間の呼び出しは、データのマーシャリングが一番のポイントです。
JNAでは、多くのJavaのプリミティブデータ型に対してCのプリミティブデータ型を自動的にマッピングしてくれます。直接マッピングできない型は、JNA独自型で対応したり、配列型のデータには、豊富な補助ユーティリティの提供でサポートされています。
以降は、マッピング方法について、紹介していきたいと思います。
■ 【1】ライブラリのマッピング
JNAを利用する流れでも説明しましたが、JNAでは、ネイティブライブラリをJavaのインターフェイスでマッピングします。そのインターフェイスは、JNA提供のLibraryインターフェイスを継承し、このインターフェイスの中にネイティブライブラリで公開している利用したいデータ構造体と呼び出したいAPIを宣言します。
後は、JNAのNative.loadLibraryメソッドで、このインターフェイスとネイティブライブラリとを関連付けます。このインターフェイスは代わりに、ネイティブライブラリの機能を公開します。
Native.loadLibrary(String nativeLibName, Class clazz)
第1引数は、ネイティブライブラリ名を指定します。Windows環境では、ネイティブライブラリ名の拡張子を取り除いて指定します。詳細および、ほかのプラットフォーム環境での場合は、JNAのドキュメントを参照してください。第2引数は、宣言した対応するJavaのインターフェイスを指定します。
図1 Javaインターフェイスとネイティブライブラリのマッピングイメージ |
■ 【2】APIのマッピング
名前とシグネチャで、APIをマッピングします。デフォルトでは、同名でマッピングしますが、JNAでは、改名のマッピング方法を提供しています。詳細は、JNAのドキュメントを参照してください。
図2 APIのマッピングイメージ |
■ 【3】プリミティブデータ型のマーシャリング
マッピングの基準は、データのサイズです。
表1 JNAのネイティブ層でのマッピング表 | ||||||||||||||||||||||||||||||
|
表2 JNA独自型のJava層でのマッピング表 | ||||||||||||||||||||||||||||||||||||
|
注意点 |
上記以外のデータ型はサポートされません。従って、ほかの型は、上記の基本型に一度に変換して、利用します。 また表1に示した自動変換は、JavaからC/C++に渡す引数に対するとき、表の通りに適用できますが、Cからの戻り値は特に配列の戻り値はそのまま受け取れません。これについては、後述の「配列型のマッピング」を参照してください。 |
1-2-3-4 |
INDEX 特集「組み込みにも役立つJavaとネイティブコードの橋渡し」 | ||
Page1 | ||
C/Sでは、サーバサイドはC/C++がまだまだ主役 JNIを使いやすくした「JNA」とは |
||
Page2 | ||
JNAを利用するための基本的な流れ JavaとC/C++のマッピング方法、6パターン 【1】ライブラリのマッピング 【2】APIのマッピング 【3】プリミティブデータ型のマーシャリング |
||
Page3 | ||
【4】構造体型のマッピング 【5】配列型のマッピング |
||
Page4 | ||
【6】入れ子構造体型のマッピング JNAアプリケーションのアーキテクチャ JNIよりも保守性を高めるJNA |
Java Solution全記事一覧 |
- 実運用の障害対応時間比較に見る、ログ管理基盤の効果 (2017/5/9)
ログ基盤の構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。今回は、実案件を事例とし、ログ管理基盤の有用性を、障害対応時間比較も交えて紹介 - Chatwork、LINE、Netflixが進めるリアクティブシステムとは何か (2017/4/27)
「リアクティブ」に関連する幾つかの用語について解説し、リアクティブシステムを実現するためのライブラリを紹介します - Fluentd+Elasticsearch+Kibanaで作るログ基盤の概要と構築方法 (2017/4/6)
ログ基盤を実現するFluentd+Elasticsearch+Kibanaについて、構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。初回は、ログ基盤の構築、利用方法について - プログラミングとビルド、Androidアプリ開発、Javaの基礎知識 (2017/4/3)
初心者が、Java言語を使ったAndroidのスマホアプリ開発を通じてプログラミングとは何かを学ぶ連載。初回は、プログラミングとビルド、Androidアプリ開発、Javaに関する基礎知識を解説する。
|
|