Narzędzia użytkownika

Narzędzia witryny


jni

Java JNI i C++

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

  • 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 <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;
 
 
}
jni.txt · ostatnio zmienione: 2008/04/16 23:55 przez chappaai