Más contenido relacionado La actualidad más candente (14) Similar a Programming JNI (20) Programming JNI2. JNIとは?
• Java Native Interface
• Javaからネイティブコードを、またはネイティブコード
からJavaを利用するための仕組み
(ネイティブコードは基本的に C/C++ を想定)
• Javaコードとネイティブコードを分離
(Javaから直接ネイティブコードは呼ばない。呼び出し
たい関数は、シェアードライブラリとしてラップする)
• JNIプログラミングって、ほとんどCプログラミング(?)
3. JNIの歴史1
• JDK1.0 JNI以前 不統一時代
Sun - Native Method Interface JVMの実装に依存してしまう。
(互換性に問題)
Netscape - Java Runtime Interface 移植性は考慮されていた
が、JVMの実装についての考慮がたりない
Microsoft - Raw Native Interface ガーベッジコレクタと相互的
に動作しなくてはいけない
4. JNIの歴史2
• JDK1.1
3社間で統一 JNI1.1が組み込まれる。すべてのJVM間でバイ
ナリ互換
ただしMicrosoftはJNIをサポートせずSunに訴えられる。
• JDK1.2
JNI1.2 Javaプラットフォームに新機能の組み込みが可能、効
率的で安定したインタフェースを確立
• JDK1.4
JNI1.4 呼び出しインタフェースの新しいエントリポイント追
加、java.nioパッケージのサポート
6. JNIで出来ること
メソッドの呼び出し
•
• フィールドへのアクセス
• オブジェクトの生成
例外の発生と捕捉
•
• JVMのロード
• スレッドの同期
8. HelloWorld (Javaコード)
$ vi HelloWorld.java
public class HelloWorld {
static {
System.loadLibrary(quot;HelloWorldquot;);
}
//Hello World表示
public native void show();
}
$ javac HelloWorld.java
※System.loadLibraryでネイティブライブラリをロード
※ネイティブメソッドにはnativeキーワードをつける
9. HelloWorld (ヘッダーファイル)
$ javah HelloWorld
$ cat 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 quot;Cquot; {
#endif
/*
* Class: HelloWorld
* Method: show
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_show(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
10. HelloWorld (Cコード)
$ vi HelloWorld.c
#include quot;HelloWorld.hquot;
JNIEXPORT void JNICALL Java_HelloWorld_show(JNIEnv *env, jobject obj)
{
printf(“Hello Worldn”);
}
$ gcc -fpic -shared -o libHelloWorld.so -I $JAVA_HOME/include
–I $JAVA_HOME/include/linux HelloWorld.c
※シェアードライブラリにすること
11. HelloWorldのテスト
$ vi test.java
public class test {
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
hello.show();
}
}
$ javac test.java
実行
$ java test
Hello World
※作ったシェアードライブラリはロード出来るようにしといてね
(LD_LIBRARY_PATH)
12. nativeメソッドの引数
• Javaコード
public native void show()
• Cコード
JNIEXPORT void JNICALL Java_HelloWorld_show(JNIEnv
*env, jobject obj)
JNIEnv *env - JNIインターフェイスポインタ
(privateなスレッドデータ、JNI関数へのポインタ)
jobject obj - オブジェクトへの参照
(staticなメソッドの場合はクラスへの参照)
14. HelloWorldのまとめ
• Javaコ ード はイ ンタ ーフ ェ イ スの定義
• 処理の実装はCコ ード に
• System.loadLibrary()でネイ ティ ブ ラ イ ブ ラ リ を
ロ ード
• ネイ ティ ブメ ソ ッ ド にはnativeキーワード
• C から Java へのアク セスはJNI関数
15. フィールドアクセス
$ vi Sample1.java
public class Sample1 {
private int data;
static {
System.loadLibrary(quot;Sample1quot;);
}
//フィールドに設定
public native void setData(int val);
//フィールドから取得
public native int getData();
}
16. フィールドに設定
$ vi Sample1.c
#include quot;Sample1.hquot;
// フィールドに設定
JNIEXPORT void JNICALL Java_Sample1_setData(JNIEnv *env, jobject obj, jint val)
{
jclass clazz;
jfieldID fid;
clazz = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, clazz, quot;dataquot;, quot;Iquot;);
(*env)->SetIntField(env, obj, fid, val);
}
17. フィールドから取得
// フィールドから取得
JNIEXPORT jint JNICALL Java_Sample1_getData(JNIEnv *env, jobject obj)
{
jclass clazz;
jfieldID fid;
jint data;
clazz = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, clazz, quot;dataquot;, quot;Iquot;);
data = (*env)->GetIntField(env, obj, fid);
return data;
}
18. フィールドアクセスのテスト
$ vi test1.java
public class test1 {
public static void main(String[] args) {
Sample1 sample = new Sample1();
sample.setData(123);
System.out.println(“data = quot; + sample.getData());
}
}
実行
$ java test1
data = 123
19. フィールド関連のJNI関数
• jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name,
const char *sig)
フィールドID取得
• NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID
fieldID)
フィールド値取得 ex) jint GetIntField()
• void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,
NativeType value)
フィールド値設定 ex) SetIntField()
※スタティックフィールド場合はGetStaticIntFieldIDのようにStatic
が付く。
※ネイティブコードからはJavaアクセス修飾子の制限を受けなくな
る。
20. フィールドアクセスのまとめ
① クラスを取得
GetObjectClass()
② フィールドIDを取得
GetFieldID()
③ フィールドの値を設定・取得
Set<type>Field() / Get<type>Field()
※C から Java へのアクセスは JNI関数を使用する。
21. JNIのネイティブ型
Java JNI
byte 8bit jbyte
short 16bit jshort
int 32bit jint
long 64bit jlong
float 32bit IEEE754 jfloat
double 64bit IEEE754 jdouble
char 16bit UNICODE jchar
boolean 8bit jboolean
void void
Object jobject
Class jclass
String jstring
jarray j<type>Array
配列
Throwable jthrowable
jvalue
jfiledID
jmethodID
22. 型シグニチャ
Java シグニチャ
byte B
short S
int I
long J
float F
double D
char C
boolean Z
void V
L<class>;
<class>の完全修飾
[<type>
<type>の配列
メ ソ ッ ド シグニチャ (引数)戻値
例えば、次のメソッドシグニチャは
long hoge(int n, String s, int[] i) → (ILjava/lang/String;[I)J
23. メソッドコール・オブジェクト生成
$ vi Sample2.java
public class Sample2 {
static {
System.loadLibrary(quot;Sample2quot;);
}
public Sample2() {
}
public Sample2(int n) {
System.out.println(quot;create object successquot;);
}
public void show() {
System.out.println(quot;method call successquot;);
}
//オブジェクトの生成
public native void createObject();
//メソッド呼び出し
public native void callMethod();
}
24. オブジェクト生成
$ vi Sample2.c
#include quot;Sample2.hquot;
//オブジェクトの生成
JNIEXPORT void JNICALL Java_Sample2_createObject(JNIEnv *env, jobject obj)
{
jclass clazz;
jmethodID mid;
jobject newObj;
clazz = (*env)->FindClass(env, quot;Sample2quot;);
mid = (*env)->GetMethodID(env, clazz, quot;<init>quot;, quot;(I)Vquot;);
newObj = (*env)->NewObject(env, clazz, mid, 0);
}
25. メソッドコール
//メソッド呼び出し
JNIEXPORT void JNICALL Java_Sample2_callMethod(JNIEnv *env, jobject obj)
{
jclass clazz;
jmethodID mid;
clazz = (*env)->GetObjectClass(env, obj);
mid = (*env)->GetMethodID(env, clazz, quot;showquot;, quot;()Vquot;);
(*env)->CallVoidMethod(env, obj, mid);
}
26. メソッドコール・オブジェクト生成の
テスト
$ vi test2.java
public class test2 {
public static void main(String[] args) {
Sample2 sample = new Sample2();
sample.createObject();
sample.callMethod();
}
}
実行
$ java test2
create object success
method call success
29. メソッド関連のJNI関数
• jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char
*name, const char *sig)
メソッドID取得
• NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID
methodID, ...)
メソッド呼び出し ex) CallVoidMethod()
※スタティックメソッドの場合はGetStaticIntMethodID()のようにStatic
が付く。
※ネイティブコードからはJavaアクセス修飾子の制限を受けなくなる。
31. 例外
$ vi Sample3.java
public class Sample3 {
static {
System.loadLibrary(quot;Sample3quot;);
}
public static final int MAX = 10;
private int[] datas = new int[MAX];
//例外を投げる
public native void setData(int no, int data) throws ArrayIndexOutOfBoundsException;
//例外を捕まえる
public native void showData(int no);
public int getData(int no) throws ArrayIndexOutOfBoundsException {
try {
return datas[no];
} catch (ArrayIndexOutOfBoundsException e) {
throw e;
}
}
}
32. 例外を投げる
$ vi Sample3.c
#include quot;Sample3.hquot;
JNIEXPORT void JNICALL Java_Sample3_setData(JNIEnv *env, jobject obj, jint no, jint data)
{
jclass clazz;
jfieldID fid;
jint *dataElems;
jintArray datas;
jboolean isCopy;
clazz = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, clazz, quot;datasquot;, quot;[Iquot;);
datas = (*env)->GetObjectField(env, obj, fid);
dataElems = (*env)->GetIntArrayElements(env, datas, &isCopy);
if (no < 0 || no >= Sample3_MAX) {
//例外投げる
jclass clazz;
jmethodID mid;
jthrowable throwObj;
clazz = (*env)->FindClass(env, quot;java/lang/ArrayIndexOutOfBoundsExceptionquot;);
mid = (*env)->GetMethodID(env, clazz, quot;<init>quot;, quot;()Vquot;);
throwObj = (*env)->NewObject(env, clazz, mid);
(*env)->Throw(env, throwObj);
return;
}
dataElems[no] = data;
if (isCopy == JNI_TRUE) {
(*env)->ReleaseIntArrayElements(env, datas, dataElems, 0);
}
}
33. 例外を捕まえる
JNIEXPORT void JNICALL Java_Sample3_showData(JNIEnv *env, jobject obj, jint no)
{
jclass clazz;
jmethodID mid;
jint data;
jthrowable throwObj;
clazz = (*env)->GetObjectClass(env, obj);
mid = (*env)->GetMethodID(env, clazz, quot;getDataquot;, quot;(I)Iquot;);
data = (*env)->CallIntMethod(env, obj, mid, no);
//例外を捕まえる
throwObj = (*env)->ExceptionOccurred(env);
if (throwObj != NULL) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return;
}
printf(quot;native data = %dnquot;, data);
}
34. 例外のテスト(1)
$ vi test3.java
public class test3 {
public static void main(String[] args) {
Sample2 sample = new Sample2();
try {
sample.setData(5, 123);
System.out.println(quot;data = quot; + sample.getData(5));
sample.setData(10, 456);
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
sample.showData(5);
sample.showData(10);
}
}
35. 例外のテスト(2)
実行
$ java test3
data = 123
java.lang.ArrayIndexOutOfBoundsException
at Sample2.setData(Native Method)
at test2.main(test2.java:7)
native data = 123
Exception in thread quot;mainquot; java.lang.ArrayIndexOutOfBoundsException
at Sample2.getData(Sample2.java:14)
at Sample2.showData(Native Method)
at test2.main(test2.java:12)
36. 例外関連のJNI関数
• jint Throw(JNIEnv *env, jthrowable obj)
例外を投げる
• jthrowable ExceptionOccurred(JNIEnv *env)
例外が発生してるか調べる
• void ExceptionDescribe(JNIEnv *env)
例外のスタックトレースをプリント
• void ExceptionClear(JNIEnv *env)
例外のクリア
※Throw()しても実際に例外を投げるのはネイティブメソッドを抜
けた後
40. CからJavaを呼ぶ(Cコード)(1)
$vi Sample4.c
#include <jni.h>
int main(int argc, char *argv[]) {
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
jclass clazz;
jobject obj;
jmethodID mid;
jint ret;
vm_args.version = JNI_VERSION_1_2;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
ret = JNI_CreateJavaVM(&jvm, (void **) &env, &vm_args);
if (ret < 0) {
fprintf(stderr, quot;Can't create JVM.quot;);
return 1;
}
41. CからJavaを呼ぶ(Cコード)(2)
clazz = (*env)->FindClass(env, quot;Sample3quot;);
mid = (*env)->GetMethodID(env, clazz, quot;<init>quot;, quot;()Vquot;);
obj = (*env)->NewObject(env, clazz, mid);
mid = (*env)->GetMethodID(env, clazz, quot;showquot;, quot;()Vquot;);
(*env)->CallVoidMethod(env, obj, mid);
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
実行
$ ./Sample4
JVM World
44. 文字列
$ vi Sample5.java
public class Sample5 {
static {
System.loadLibrary(quot;Sample5quot;);
}
//Javaからの文字列表示
public native void show(String str);
//Cから文字列取得
public native String getMessage();
}
45. 文字列(Javaから)
$ vi Sample5.c
#include <iconv.h>
#include quot;Sample5.hquot;
// Javaからの文字列表示
JNIEXPORT void JNICALL Java_Sample5_show(JNIEnv *env, jobject obj, jstring str)
{
const char *msg;
jboolean isCopy;
msg = (*env)->GetStringUTFChars(env, str, &isCopy);
if (isCopy == JNI_TRUE) {
iconv_t cd;
size_t isize, osize;
char euc[25];
char *obuf = euc;
isize = (*env)->GetStringUTFLength(env, str) * 2;
osize = sizeof(euc);
cd = iconv_open(quot;euc-jpquot;, quot;utf-8quot;);
iconv(cd, &msg, &isize, &obuf, &osize);
printf(quot;%snquot;, euc);
(*env)->ReleaseStringUTFChars(env, str, msg);
iconv_close(cd);
}
}
46. 文字列(Cから)
// Cから文字列取得
JNIEXPORT jstring JNICALL Java_Sample5_getMessage(JNIEnv *env, jobject obj)
{
const char *msg = quot;シー ワールドquot;;
char utf[25];
char *obuf = utf;
iconv_t cd;
size_t isize, osize;
isize = strlen(msg);
osize = sizeof(utf);
memset(utf, 0, osize);
cd = iconv_open(quot;utf-8quot;, quot;euc-jpquot;);
iconv(cd, &msg, &isize, &obuf, &osize);
iconv_close(cd);
return (*env)->NewStringUTF(env, utf);
}
47. 文字列のテスト
$ vi test5.java
public class test5 {
public static void main(String[] args) {
Sample5 sample = new Sample5();
sample.show(quot;ジャバ ワールドquot;);
System.out.println(sample.getMessage());
}
}
実行
$ java test5
ジャバ ワールド
シー ワールド
48. 文字列関連のJNI関数
• jstring NewStringUTF(JNIEnv *env, const char *bytes)
UTF-8の文字列からStringオブジェクトを生成
• const char* GetStringUTFChars(JNIEnv *env, jstring string,
jboolean *isCopy)
StringオブジェクトからUTF-8文字列を取得
• void ReleaseStringUTFChars(JNIEnv *env, jstring string,
const char *utf)
GetStringUTFChars()で取得した文字列を開放
• jsize GetStringUTFLength(JNIEnv *env, jstring string)
文字列の長さを取得
※C側で扱える文字コードに変換する必要あり
(今回はiconvを使用した。)
52. 今日のまとめ
処理の実装はCコード側
•
• C から Java へのアクセスはJNI関数
• やっぱりCプログラミングだった(?)
JNIプログラミングは難しくない(?)
•
• でも面倒だ
53. 参考文献
• JNI : Java Native Interfaceプログラミング
著者 ロブ・ゴードン 出版社 ピアソン
• JDKドキュメント
http://java.sun.com/j2se/1.4/ja/docs/ja/guide/jni/index.html
54. おわり
Ver.1.0 2002/08/22
Ver.1.1 2002/08/27