Autor : Łukasz Mosdorf G1ISI — Ł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
Aby uruchomić przykładowy program wykorzystujący JNI należy:
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 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); } }
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 <jni.h> /* 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 <iostream> #include <jni.h> using namespace std; class Lol { private: int i; public: void changei(int arg) { i=arg; cout<<"W obiekcie Lol, zmieniono wartosc i na: "<<i<<endl; } }; //Prosta metoda cpp wywolywana z poziomu javy //Przykład podstawowej składni metody napisanej w C++ (C) i wołanej z Javy JNIEXPORT void JNICALL Java_jni_1pkg_Jni_native_1call (JNIEnv * env, jclass cls) { cout<<"Najprostsza metoda c++ wywołana z javy !!"<<endl; } //Metoda zmieniajaca wartosc Stringa Javy podanego jako argument JNIEXPORT jstring JNICALL Java_jni_1pkg_Jni_change_1string (JNIEnv * env, jclass jCl, jstring inStr) { char buf[128]; const char *str; /* Przeksztalcamy String javowy na ciąg bajtów*/ str = env->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"<<endl; scanf("%s", buf); return env->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<<endl; Lol* lol = new Lol(); if (!lol) { return (jint)1; //nie udalo sie zaalokowac } //zwracamy wskaznik zaalokowanego obiektu env->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"<<endl; env->CallVoidMethod(jObj, mid); cout<<"W C++, po Callbacku"<<endl; } JNIEXPORT jint JNICALL Java_jni_1pkg_Jni_deleteCppObj (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 na obiekt ktory usuwamy to: "<<(int)ptr<<endl; Lol* lol = reinterpret_cast<Lol*>(ptr); delete lol; //zwracamy wskaznik zaalokowanego obiektu env->SetIntField(jObj, fid, (jint)0); return (jint)0; }