====== Java JNI i C++ ======
Autor : Łukasz Mosdorf G1ISI
---
//[[L.Mosdorf[eeeet]stud.elka.pw.edu.pl|Łukasz Mosdorf]] 2008/04/13 01:05//
Ta strona zawiera prosty przykład zastosowania Java Native Interface. Technologia ta pozwala łączyć kod Javy z kodem pisanym w językach specyficznych dla danej platformy. Potrzeba łączenia Javy z C++ zachodzi np gdy
* maszyna wirtualna nie jest w stanie zapewnić nam wystarczającej szybkości obliczeń
* Chcemy połączyć aplikację javy z juz istniejącym kawałkiem oprogramowania napisanego w C/C++
===== Uruchamianie =====
Aby uruchomić przykładowy program wykorzystujący JNI należy:
* Utworzyć klasę (Javaclass.java), która deklaruje metodę natywną
* skompilowac tę klasę za pomocą polecenia 'javac' i w ten sposób otrzymać Javaclass.class
* uzyc javah -jni aby otrzymac plik naglowkowy, w oparciu o który napiszemy kod w C++
* napisać kod w C++
* skompilować kod do postaci biblioteki (*.dll pod Windows, *.so pod Solarisem)
* uruchomić program Javaclass
Przykładowa seria poleceń potrzebna do kompilacji przykładu na platformie Windows:
javac .\jni_pkg\Jni.java
javah -jni jni_pkg.Jni
cl -I "c:\Program Files\Microsoft Visual Studio 9.0\VC\include" -I "c:\j2sdk1.4.2_17\include
-I"c:\j2sdk1.4.2_17\include\win32" -LD jni_pkg_Jni.cpp -FeJni_lib.dll''
Jeżeli powyższe polecenia nie dzialaja, należy dodać odpowienie ścieżki do zmiennej srodowiska Windows - PATH.
Polecenie cl polecam uruchamiac z konsoli Visual Studio - nie mamy wtedy problemów z wskazywaniem lokalizacji dodatkowych plików nagłówkowych i bibliotek.
===== Kod Java =====
Kod zawiera kilka deklaracji przykladowych metod natywnych które sa następnie w nim wykorzystywane.
/**
* @author Lukasz Mosdorf G1ISI
*/
/**
* Program ilustrujący Zastosowanie JNI - Java Native Interface.
* Kod wywołuje kilka natywnych metod napisanych w cpp
*/
package jni_pkg;
public class Jni {
public int myInt = 10;
public static String mystring = "JNI example";
public int[] intArray;
private static native void native_call(); // sample native call
private static native String change_string(String arg);
private native int sumArray(int[] arr);
private native int initCppObj();
private native int deleteCppObj();
private native int changeCppObjField(int a);
private native void callMe();
public int cppObjPtr;
private void callback()
{
System.out.println("Java wywolana z cpp");
}
//Static- wywolywane na saym poczatku
static {
System.loadLibrary("Jni_lib");
}
public static void main(String[] args)
{
Jni jni = new Jni();
int arr[] = new int[10];
//wywołanie metody natywnej, która zmienia podany obiekt String
String input = jni.change_string("String wyslany do metody natywnej");
System.out.println("Dostano w Javie zmieniony string: "+ input);
//Zapełnienie tablicy danymi i wywołanie natywnje funkcji sumującej
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
int sum = jni.sumArray(arr);
System.out.println("Suma tablicy zwrocona z metody natywnej = " + sum);
//inicjalizaja obiektu lol, wskaznik przechowywany w cppObjPtr
jni.initCppObj();
System.out.println("Obiekt lol zaalokowany: "+ jni.cppObjPtr);
//Wywołanie metody obiektu uprzednio zaalokowanego,
//ktora zmienia wartosc jego pola
jni.changeCppObjField(15);
//wołanie metody która wywołuje motodę obiektu wołającego
jni.callMe();
jni.deleteCppObj();
System.out.println("Obiekt lol usuniety, ptr: "+ jni.cppObjPtr);
}
}
===== Kod Cpp =====
Poniżej przedstawiono plik nagłówkowy wygenerowany automatycznie przy uzyciu polecenia javah -jni jni_pkg.Jni.
/*Lukasz Mosdorf G1ISI
*Plik "jni_pkg_Jni.h" generowany
*przy wywolaniu polecenia javah -jni jni_pkg.Jni
*/
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class jni_pkg_Jni */
#ifndef _Included_jni_pkg_Jni
#define _Included_jni_pkg_Jni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jni_pkg_Jni
* Method: native_call
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jni_1pkg_Jni_native_1call
(JNIEnv *, jclass);
/*
* Class: jni_pkg_Jni
* Method: change_string
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_jni_1pkg_Jni_change_1string
(JNIEnv *, jclass, jstring);
/*
* Class: jni_pkg_Jni
* Method: sumArray
* Signature: ([I)I
*/
JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_sumArray
(JNIEnv *, jobject, jintArray);
/*
* Class: jni_pkg_Jni
* Method: initCppObj
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_initCppObj
(JNIEnv *, jobject);
/*
* Class: jni_pkg_Jni
* Method: deleteCppObj
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_deleteCppObj
(JNIEnv *, jobject);
/*
* Class: jni_pkg_Jni
* Method: changeCppObjField
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_changeCppObjField
(JNIEnv *, jobject, jint);
/*
* Class: jni_pkg_Jni
* Method: callMe
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jni_1pkg_Jni_callMe
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Ciała metod natywnych znajdują się w pliku cpp pzedstawionym poniżej:
/**
*Łukasz Mosdorf G1ISI
*Plik "jni_pkg_Jni.cpp" zawierajacy definicje klasy i kilka metod natywnych
*wolanych z Javy. Załączamy plik "jni_pkg_Jni.h" generowany
*przy wywolaniu polecenia javah -jni jni_pkg.Jni
*/
#include "jni_pkg_Jni.h"
#include
#include
using namespace std;
class Lol
{
private:
int i;
public:
void changei(int arg)
{
i=arg;
cout<<"W obiekcie Lol, zmieniono wartosc i na: "<GetStringUTFChars(inStr, NULL);
if (str == NULL) {
return NULL; // OutOfMemoryError juz rzucony
}
printf("W metodzie cpp String: %s \n", str);
//Zwalniamy obiekt aby mogl sie nim zajac juz Garbage Collector
env->ReleaseStringUTFChars(inStr, str);
//Zakladamy ze uzytkownik nie wpisze wiecej niz 127 znakow...
cout<<"Wprowadz nowego Stringa"<NewStringUTF(buf);
}
//Prosta metoda sumująca tablice intów 10-elementową podana z poziomy Javy jako argument
JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint buf[10];
jint i, sum = 0;
//pobieramy tablice
env->GetIntArrayRegion(arr, 0, 10, buf);
//sumujemy
for (i = 0; i < 10; i++) {
sum += buf[i];
}
return sum;
}
/*Metoda inicjalizująca obiekt lol, i zapisujaca miejsce jego alokacji w pamieci
* w polu cppObjPtr obiektu który tę metodę wywolal.
*/
JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_initCppObj (JNIEnv *env , jobject jObj)
{
jfieldID fid; //magazynujemy id pola klasy javowej
/* pobieramy referencje na klase obiektu jObj */
jclass cls = env->GetObjectClass(jObj);
/* Pobieramy ID pola klasy*/
fid = env->GetFieldID(cls, "cppObjPtr", "I");
if (fid == NULL) {
return (jint)1; /* nie udalo sie zdobyc id pola */
}
jint ptr = env->GetIntField(jObj, fid);
cout<<"poczatkowa wartosc wskaznika to: "<<(int)ptr<SetIntField(jObj, fid, (jint)lol);
return (jint)0;
}
//Prosta metoda wywołująca metodę obiektu lol (uprzednio zaalokowanego). Metoda zmienia wartość pola obiektu
JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_changeCppObjField (JNIEnv* env, jobject jObj, jint k)
{
/*pzrechowujemy id pola klasy javowej*/
jfieldID fid;
/* pobieramy referencje na klase obiektu jObj */
jclass cls = env->GetObjectClass(jObj);
/* pobieramy field ID*/
fid = env->GetFieldID(cls, "cppObjPtr", "I");
if (fid == NULL) {
return (jint)1; /* nie udalo sie zdobyc id pola */
}
/*Pobieramy wskaznik na nasz obiekt (zapisany w polu klasy wywolujacej metode)*/
jint ptr = env->GetIntField(jObj, fid);
Lol* lol = (Lol*)ptr;
/*wywolujemy metodę obiektu lol*/
lol->changei(k);
return 0;
}
//Metoda realizujaca tzw CALLBACK, czyli wywołanie metody nalezacej do obiektu javy
JNIEXPORT void JNICALL Java_jni_1pkg_Jni_callMe (JNIEnv * env, jobject jObj)
{
jclass cls = env->GetObjectClass(jObj);
//pobieramy ID metody
jmethodID mid = env->GetMethodID(cls, "callback", "()V");
if (mid == NULL) {
return; // nie znaleziono metody
}
cout<<"W C++, przed Callback'iem"<CallVoidMethod(jObj, mid);
cout<<"W C++, po Callbacku"<GetObjectClass(jObj);
/* Pobieramy ID pola klasy*/
fid = env->GetFieldID(cls, "cppObjPtr", "I");
if (fid == NULL) {
return (jint)1; /* nie udalo sie zdobyc id pola */
}
jint ptr = env->GetIntField(jObj, fid);
cout<<"poczatkowa wartosc wskaznika na obiekt ktory usuwamy to: "<<(int)ptr<(ptr);
delete lol;
//zwracamy wskaznik zaalokowanego obiektu
env->SetIntField(jObj, fid, (jint)0);
return (jint)0;
}