JNIで呼び出したCの関数からJavaのメソッドをコールバックする
やりたいことは、Javaからnative呼び出し
呼び出されたCの関数から、呼び出し元のJavaにあるメソッドを呼び出し
予想外に苦戦したので、メモしておきます。
MinGWのインストール
Cのビルドは、MinGWで行いました。
Symfoware Windows 7にMinGWをインストールする(mingw-get-inst使用)
また、以前JNIを作成したときの記事は以下の通り。
Symfoware MinGWで作成したDLLをJavaからJNI経由で呼び出す
Javaのサンプルプログラム
Java側のサンプルプログラムはこんな感じになりました。
package jnitest;
public class MainProccess {
//ライブラリをロード
static {
System.loadLibrary("test");
}
public static void main(String[] args) throws InterruptedException {
System.out.println("main start");
MainProccess proccess = new MainProccess();
for (int i = 0; i < 10; i++) {
synchronized (proccess) {
System.out.println("main:" + i);
//C言語の関数呼び出し
add10(proccess, i);
}
//特に意味はないが、実行結果をじっくり確認するため待つ
Thread.sleep(500);
}
System.out.println("main end");
}
//JNI呼び出し
protected static native void add10(MainProccess listener, int value);
// C++から呼ばれるメソッド
public synchronized void callback(int n) {
System.out.println("callback:" + n);
}
}
「callback」がC側から呼び出す予定のメソッドです。
Cのサンプルプログラム
Javaのプログラムを作成したら、ビルドしてclassファイルを作成します。
その後、javahコマンドでCのヘッダーファイルを作成します。
"C:\Program Files\Java\jdk1.6.0_24\bin\javah.exe" -classpath . jnitest.MainProccess
出力される「jnitest_MainProccess.h」の内容はこんな感じ。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jnitest_MainProccess */
#ifndef _Included_jnitest_MainProccess
#define _Included_jnitest_MainProccess
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jnitest_MainProccess
* Method: add10
* Signature: (Ljnitest/MainProccess;I)V
*/
JNIEXPORT void JNICALL Java_jnitest_MainProccess_add10
(JNIEnv *, jclass, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
JNIで呼び出されるCプログラム「jnitest_MainProccess.c」はこんな感じ。
#include <jni.h>
#include "jnitest_MainProccess.h"
JNIEXPORT void JNICALL Java_jnitest_MainProccess_add10
(JNIEnv *env, jclass cls, jobject obj, jint value) {
//リスナーのlistenメソッドを取得する
jmethodID func = (*env)->GetMethodID(env, cls, "callback", "(I)V");
if(func==NULL) return;
(*env)->CallVoidMethod(env, obj, func, value + 10);
}
dllにするため、jnitest_MainProccess.defを作成しておきます。
LIBRARY test.dll
EXPORTS
Java_jnitest_MainProccess_add10@16 @ 1
Makefileはこんな感じ。
CC=gcc
RM=del
CPPFLAGS= -I"C:\Program Files\Java\jdk1.6.0_24\include" -I"C:\Program Files\Java\jdk1.6.0_24\include\win32"
all: test.dll
test.dll: jnitest_MainProccess.o
dllwrap.exe -k -def jnitest_MainProccess.def --driver-name gcc -o test.dll jnitest_MainProccess.o
jnitest_MainProccess.o: jnitest_MainProccess.c
$(CC) $(CPPFLAGS) -c jnitest_MainProccess.c -o jnitest_MainProccess.o $(LDFLAGS)
clean:
$(RM) *.o *.bak *~ core TAGS
distclean:
$(RM) *.o *.bak *~ core TAGS
$(RM) julius-simple
※実際は4スペースではなくタブ。
コマンドプロンプトで、mingw32-makeを実行し、dllを作成します。
c:\>mingw32-make
gcc -I"C:\Program Files\Java\jdk1.6.0_24\include" -I"C:\Program Files\Java\jdk1.
6.0_24\include\win32" -c jnitest_MainProccess.c -o jnitest_MainProccess.o
dllwrap.exe -k -def jnitest_MainProccess.def --driver-name gcc -o test.dll jnite
st_MainProccess.o
これで「test.dll」が出来上がります。
実行してみる
c:\>java -cp . -Djava.library.path=[test.dllのあるパス] jnitest.MainProccess
main start
main:0
callback:10
main:1
callback:11
main:2
callback:12
main:3
callback:13
main:4
callback:14
main:5
callback:15
main:6
callback:16
main:7
callback:17
main:8
callback:18
main:9
callback:19
main end
狙い通りの動作です。
【参考URL】
JNI JavaVMメモ(Hishidama's Java native interface JVM Memo)
- 関連記事
コメント