JNI Beispiel

Das folgende Beispiel zeigt, wie aus Java heraus eine C-Methode in einer DLL aufgerufen wird und aus einer C-Methode wiederum eine Java-Methode gerufen wird. Das Beispiel zeigt zum Einen den Aufruf einer Java Methode des rufenden Java-Objektes, als auch den Aufruf einer Java-Methode eines anderen, neu zu erzeugenden Java-Objekts.

Beschreibung

Die Java Klasse Start.java bindet die C-DLL und deren Methoden ein und dient somit als Schnittstelle von Java nach C. Die Methode callback in Start wird von C aus aufgerufen. Die Klasse JavaFromC implementiert eine Java-Methode callback, die ebenfalls von C aus aufgerufen wird. Dazu muss aber in C zunächst ein Objekt dieser Klasse erzeugt werden. cmodule.cpp implementiert eine in Java einbindbare DLL, die die in Start.java deklarierten Methoden callCWithCallback()und callCWithCallbackInOtherClass()implementiert.

Programmierung

Start.java

Die Klasse Start bindet die C-DLL cmodule.dll mit loadLibrary ein. Diese muss im selben Verzeichnis wie Start.class stehen. Anschliessend werden die C-Methoden als native deklariert. In der Methode main wird ein Objekt der Klasse Start erzeugt und nacheinander die C-Routinen callWithCallback() und callWithCallbackInOtherClass aufgerufen. Die C-Methode callWithCallback ruft wiederum die Methode callback() von Start.
Tipp: Bei der Verwendung von Eclipse muß im Kontextmenü Run As-> Run.. das Häkchen bei "Include libraries when searching for a main class" gesetzt sein.
Quelltext:

package test;
public class Start {
package test;
public class JavaFromC {

static {
System.loadLibrary("cmodule");
}
public native int callCWithCallback();
public native int callCWithCallbackInOtherClass();

public static void main(String[] args) {
Start c = new Start(); System.out.println("In Java: Start.main"); c.callCWithCallback(); c.callCWithCallbackInOtherClass();
}
public void callback(){
System.out.println("In Java: Start.callback");
}

}

JavaFromC.java

Die C-Methode callWithCallbackInOtherClass erzeugt eine Instanz dieser Klasse und ruft die Methode callback.
Quelltext:

public JavaFromC(){
}
public void callback(){
System.out.print("In JavaFromC.callback");
}

}

cmodule.h

Der C-Header cmodule.h wird durch den Aufruf von javah -jni -o cmodule.h test.Start automatisch erzeugt. javah.exe ist ein mit dem jdk ausgeliefertes Werkzeug und steht im bin Verzeichnis des jdk. Die dort erzeugten Methodendeklarationen müssen in cmodule.cpp implementiert werden. Das Ergebnis sieht wie folgt aus:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class test_Start */
#ifndef _Included_test_Start
#define _Included_test_Start
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: test_Start
* Method: callCWithCallback
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_test_Start_callCWithCallback (JNIEnv *, jobject);
/*
* Class: test_Start
* Method: callCWithCallbackInOtherClass
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_test_Start_callCWithCallbackInOtherClass (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include <jni.h>
#include "cmodule.h"
#include <stdio.h>
JNIEXPORT jint JNICALL Java_test_Start_callCWithCallback (JNIEnv * env, jobject obj){

cmodule.cpp

cmodule implementiert die Funktionsdeklarationen des automatisch erzeugten cmodule.h. Um aus C heraus eine Methode aufzurufen, benötigt man deren methodId, die man über die Methode getMethodID bekommt. Die Methode getMethodID hat als Parameter die Klasse in der die Methode definiert wird, den Methodennamen und als dritten Parameter deren Signatur. Die Signatur kann man mit dem Werkzeug javap ermitteln. Der Aufruf javap -s -p test.Start erzeugt folgende Ausgabe:
Compiled from "Start.java"
public class test.Start extends java.lang.Object{
static {};
Signature: ()V
public test.Start();
Signature: ()V
public native int callCWithCallback();
Signature: ()I
public native int callCWithCallbackInOtherClass();
Signature: ()I
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
public void callback();
Signature: ()V
}

Mit der MethodID und einem Java Objekt kann dann die Methode je nach Rückgabewert mittels einer CallXXXMethode aufgerufen werden. Die Methodennamen lauten:
CallVoidMethod() void
CallObjectMethod() jobject
CallBooleanMethod() jboolean
CallByteMethod() jbyte
CallCharMethod() jchar
CallShortMethod() jshort
CallIntMethod() jint
CallLongMethod() jlong
CallFloatMethod() jfloat
CallDoubleMethod() jdouble

Der Aufruf von Konstruktoren erfolgt prinzipiell genauso wie bei normalen Methoden. Besonderheiten:
Der Konstruktor liefert ein jobject zurück, deshalb wird er mit CallObjectMethod aufgerufen. Anstelle eines Objektes wird beim Aufruf ein jclass Objekt benötigt, das man über FindClass("Klassenname") ermitteln kann. Der Methodenname ist "<init>". Die Signatur lässt sich über den Aufruf von javap (s.o.) ermitteln.
Quelltext:

jclass cls;
jmethodID mid;
printf( "Jetzt sind wir in callCWithCallbackC " );
cls=env->GetObjectClass(obj);//jclass des rufenden Objekts besorgen
mid=env->GetMethodID(cls, "callback", "()V"); //jmethodID der java methode callback besorgen
env->CallVoidMethod(obj,mid); //methode callback im rufenden Objekt aufrufen
return 0;

}
JNIEXPORT jint JNICALL Java_test_Start_callCWithCallbackInOtherClass (JNIEnv * env, jobject obj){

jclass cls;
jmethodID mid;
jobject jfcObj;
printf( "Jetzt sind wir in callCWithCallBackInOtherClassC " );
cls = env->FindClass("test/JavaFromC"); //jclass von JavaFromC besorgen
mid=env->GetMethodID(cls, "", "()V"); //jmethodID des Konstruktors besorgen
jfcObj = env->NewObject(cls, mid); //Objekt der Klasse JavaFromC erzeugen
mid=env->GetMethodID(cls, "callback", "()V"); //jmethodID der java methode callback besorgen
env->CallVoidMethod(jfcObj,mid);//methode callback im erzeugten Objekt aufrufen
return 0;

}

Download VC++ Projekt cmodule.dll
Sollten bei der Verwendung Fehler auftreten, melden Sie diese bitte per Mail an: Dieter Ebhart