19.03 JNI Parameterübergabe
Nachdem Sie bereits ein einfaches JNI Beispiel erstellt und erfolgreich ausgeführt haben, steigen wir in diesem Kapitel ein wenig tiefer in die Materie ein. Wir beschäftigen uns mit Übergabeparameter von Java an eine native Bibliothek und dem Rückgabewert der nativen Bibliothek an Java.
Als Beispiel verwenden wir einen simplen Taschenrechner in Form einer C-Library, dem zwei Ganzzahlen und ein Operator übergeben werden können. Je nach Operator werden die Zahlen addiert, subtrahiert, dividiert oder multipliziert.
Die Java Klasse wird ganz normal mit einer nativen Methode zur Berechnung inkl. der gewünschten Parameter und des Rückgabewerts (einem int
als Ergebnis) erzeugt. Zusätzlich definieren wir zu Testzwecken gleich noch eine Main Methode:
package de.jbb.jni; public class SimpleParameterTest { public static void main(String[] args) { SimpleParameterTest spt = new SimpleParameterTest(); System.out.println(spt.calc(10, 5, "+")); System.out.println(spt.calc(6, 3, "/")); System.out.println(spt.calc(2, 5, "-")); System.out.println(spt.calc(7, 6, "*")); } static { System.loadLibrary("calculator"); } public native int calc(int first, int second, String operator); }
Nachdem Sie die Klasse kompiliert und die dazugehörige Header-Datei (wie im letzten Kapitel beschrieben) mit javah
erzeugt haben, können wir uns dem eigentlich interessantem Teil, dem C-Programm widmen. Dabei ist zu beachten, dass Die Datentypen nicht einfach von Java nach C übernommen werden können. Aus einem Java int
wird in C ein jint
, aus einem boolean
ein jboolean
. Anbei finden Sie eine Übersicht der jeweiligen Derivate, die direkt weiterverwendet werden können.
„Weiterverwendet“ bedeutet in diesem Fall, dass mit den Typen sofort gearbeitet werden kann, ohne dass sie vorher in irgendeiner Weise konvertiert werden müssen.
Java | Native |
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
void | void |
Mit diesem Wissen können wir eine C-Library schreiben, die mit unseren beiden Zahlen rechnen kann. Lediglich der String
(nativ: jstring
) wurde noch nicht berücksichtigt. Deshalb wird der Operator fürs Erste ignoriert. Es soll genügen, wenn am Anfang einfach beide Zahlen addiert werden. Als Rückgabewert wird ebenfalls wieder ein jint
verwendet. Dieser wird dann automatisch in einen int
konvertiert.
#include <jni.h> #include "de_jbb_jni_SimpleParameterTest.h" JNIEXPORT jint JNICALL Java_de_jbb_jni_SimpleParameterTest_calc( JNIEnv *env, jobject obj, jint first, jint second, jstring operator) { return first + second; }
Nach der Kompilierung der Library können wir das Programm ausführen. Wir erhalten als Ausgabe:
15
9
7
13
Um den Rechner zu vervollständigen, muss noch der Operator interpretiert werden. Mit der C-Methode strcmp
kann man zwei Zeichenketten vergleichen. Bleibt die Frage, wie der jstring
in eine C-Zeichenkette (char
-Array) umgewandelt werden kann. Hierfür wird eine Funktion der JNI Umgebung (JNIEnv
) eingesetzt. Mit dem Aufruf GetStringUTFChars
kann ein in Unicode kodierter Java String
(16 Bit) in ein UTF-8 C-char
-Array (8 Bit) umgewandelt werden.
const char *c_op = (*env)->GetStringUTFChars(env, operator, 0);
Bei Funktionsaufrufen der JNI Umgebung wird in C standardmäßig zuerst die JNI Umgebung selbst übergeben (in C++ kann darauf verzichtet werden). Anschließend folgt hier der zu konvertierende jstring
und ein Flag, ob eine Kopie angelegt werden soll oder nicht.
Jetzt können Sie den simplen Taschenrechner vervollständigen:
#include <jni.h> #include <string.h> #include "de_jbb_jni_SimpleParameterTest.h" JNIEXPORT jint JNICALL Java_de_jbb_jni_SimpleParameterTest_calc(JNIEnv *env, jobject obj, jint first, jint second, jstring operator) { const char *c_op = (*env)->GetStringUTFChars(env, operator, 0); jint ret = 0; if (strcmp(c_op, "-") == 0) { ret = first - second; } if (strcmp(c_op, "+") == 0) { ret =first + second; } if (strcmp(c_op, "/") == 0) { ret = first / second; } if (strcmp(c_op, "*") == 0) { ret =first * second; } (*env)->ReleaseStringUTFChars(env, operator, c_op); return ret; }
Ausgabe:
15
2
-3
42
Im nächsten Kapitel werden Sie weitere Funktionen der JNI Umgebung kennenlernen.