Zenedith's dev blog

Dałem się namówić.., szatanowi chyba..

JNI #2 – Stawiamy JVM

Pierwszą część mamy już za sobą. Potrafimy już tworzyć skomplikowane skrypty w Javie i korzystać z natywnego wsparcia ze strony cpp. Pozostaje więc teraz cały proces odpalić z exe..Stworzymy teraz aplikację startującą JVM i nasze skrypty – otrzymamy więc możliwość stworzenia „silnia gry” z Javą jako językiem skryptowym! Do dzieła.

Na początek wypadałoby stworzyć kod w Javie, który będzie startował cały skrypt w Javie – może to być jakaś ogólna klasa typy Game, która tworzy resztę managerów i logikę gry.
„Wołanie” metod statycznych z Javy jest bardzo proste z tego powodu, że nie musimy mieć instancji(obiektu) danej klasy. I na tym się właśnie zatrzymamy w tej części tutoriala.
Załóżmy więc, że mamy taki kod:

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.UIManager;

public class Game {

public static void main(String[] args) {
//rusza nasza logika..

	try {
		UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");

		JFrame jFrame = new JFrame();
		jFrame.addWindowListener(
		new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
			System.exit(0);
			}
		}
		);

		jFrame.setSize(400, 300);
		jFrame.setVisible(true);

	} catch (Exception e) {
		e.printStackTrace();
	} catch (Throwable t) {
		t.printStackTrace();
	}
}

public static int GetInt(int n){
	return n*n;
}

public static int GetVersion(){
	return 1;
}

}

Zauważmy, że wszystkie metody są statyczne oraz dobrane tak, żeby pokazać kluczowe różnice w ich wywoływaniu z poziomu cpp.
Teraz przejdźmy do naszego projektu w cpp – tworzymy nowy, pusty projekt c++ wg poprzednio podanych schematów. Tym razem musimy zrobić następujące czynności:

  • na zakładce C/C++ Bulid przechodzimy do Settings/Tool Settings,
  • przechodzimy do GCC C++ Compiler i dalej do Directories,
  • klikając przycisk dodajemy katalogi \include i \include\win32 – (w moim przypadku są to D:\Program Files\Java\jdk1.6.0_06\include oraz D:\Program Files\Java\jdk1.6.0_06\include\win32)
  • zatwierdzamy wprowadzone zmiany,
  • następnie odnajdujemy zakładkę MinGW C++ Linker, która jest na poziomie C/C++ Bulid,
  • wybieramy pole Libraries i uzupełniamy obie rubryki: w górej wpisujemy nazwę biblioteki statycznej którą musimy dołączyć do projektu ze względu na JVM – wpisujemy jvm (bez rozszerzenia), natomiast w dolnej musimy wskazać katalog zawierający tą bibliotekę (\lib w katalogu zainstalowanego JDK – u mnie D:\Program Files\Java\jdk1.6.0_06\lib),
  • zatwierdzamy wprowadzone zmiany i powracamy do ekranu głównego.

Dodajemy do projektu pliki implementacji – w naszym przypadku wystarczy jeden, który wystartuje JVM – np. main.cpp. Jego treść jest u mnie następująca:

#include <jni.h>
#include <conio.h>
#include <memory.h>

int main()
{
JavaVMOption options[1];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
jint square;

options[0].optionString = "-Djava.class.path=.";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 1;
vm_args.options = options;
status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

if (status != JNI_ERR)
{
printf("jvm ok\n");

cls = env->FindClass("Game");
if(cls !=0)
{
printf("class Game ok\n");

mid = env->GetStaticMethodID( cls, "GetInt", "(I)I");
if(mid !=0)
{
square = env->CallStaticIntMethod(cls, mid, 5);
printf("GetInt: %d\n", square);
}

mid = env->GetStaticMethodID( cls, "GetVersion", "()I");
if(mid !=0)
{
square = env->CallStaticIntMethod(cls, mid);
printf("GetVersion: %d\n", square);
}

mid = env->GetStaticMethodID( cls, "main","([Ljava/lang/String;)V");

if(mid !=0)
{
printf("method main ok\n");

jclass string = env->FindClass("java/lang/String");
jobjectArray args = env->NewObjectArray(0,string, NULL);

env->CallStaticIntMethod(cls, mid, args);
}
}

jvm->DestroyJavaVM();
return 0;
}
else{
printf("jvm problem");
return -1;
}

}

Teraz będę opowiadał o tym co się w nim dzieje.
Na starcie występuje deklaracja zmiennych, które za chwilę będą użyte do tworzenia JVM. Przejdźmy więc od razu do linii ustawiającej opcje JVM:

options[0].optionString = „-Djava.class.path=.”;

Definiuje ona ścieżkę, gdzie JVM będzie szukać naszej klasy – u mnie widnieje . (kropka) a więc będzie szukać w katalogu projektu (cpp). Możemy tu oczywiście dodać parę ścieżek, rozdzielonych średnikiem (windows) lub dwukropkiem (linux,mac). Ponieważ jest to tablica opcji (o zadeklarowanym wymiarze 1) to nasza opcja ma indeks 0. Gdybyśmy chcieli zwiększyć ilość opcji to musimy to uwzględnić w miejscu deklaracji zmiennej options oraz parę linii niżej, gdzie podajemy ich liczbę raz jeszcze, już bezpośrednio (vm_args.nOptions = 1;).

Przejdźmy teraz do linii zawierającej następujący wpis:
vm_args.version = JNI_VERSION_1_4;
Jest to dość kłopotliwa stała, ponieważ jeśli ją źle podamy to nie nastąpi stworzenie JVM. Jeśli będziemy kompilować nasz kod z wykorzystaniem JDK6, wartość ta powinna być ustawiona na JNI_VERSION_1_6, ja natomiast kompilowałem z użyciem JDK1.4 i JDK6 (co można zmienić w opcjach projektu Javy lub globalnie w opcjach Eclipse) i działa z JNI_VERSION_1_4.
Do momentu szukania pierwszej metody wszystko powinno być jasne. Zobaczmy więc pierwszy przykład:

mid = env->GetStaticMethodID( cls, „GetInt”, „(I)I”);

Mówi on tak: znajdź mi „wzór” metody statycznej z określonej wcześniej klasy o nazwie „GetInt”, która pobiera parametr typu int i zwraca wynik typu int.
Jeśli znaleziono, mamy kolejną linię:

square = env->CallStaticIntMethod(cls, mid, 5);

której tłumaczenie jest następujące: wywołaj metodę statyczną zwracającą int z określonej wcześniej klasy i „wzorca” metody i przekaż jej parametr 5 (int).Wynik zapisz do zmiennej square.
To teraz w skrócie dwa kolejne przykłady:

mid = env->GetStaticMethodID( cls, „GetVersion”, „()I”);
square = env->CallStaticIntMethod(cls, mid);

co tłumaczymy jako: znajdź „wzór” metody statycznej o nazwie „GetVersion” nie pobierającej żadnego parametru (pusty nawias zamiast V jak void!) i zwracającą wynik typu int. Wywołaj metodę z tego „wzorca” i zapisz wynik do zmiennej square.
Na zakończenie metoda main z Javy:

mid = env->GetStaticMethodID( cls, „main”,”([Ljava/lang/String;)V”);
jclass string = env->FindClass(„java/lang/String”);
jobjectArray args = env->NewObjectArray(0,string, NULL);
env->CallStaticIntMethod(cls, mid, args);

Zaczynamy od poszukiwania „wzorca”, podając że chodzi nam o metodę statyczną o nazwie „main”, przyjmującą parametr typu String i nic nie zwracającej (V od void). Następnie musimy znaleźć „wzorzec” klasy String, który zostanie użyty do utworzenia pustej tablicy obiektów typu String, a następnie przekazanej do wywołania metody.

Reklamy

31 Lipiec, 2008 - Posted by | cpp, engine, game, java, scripts | , , , ,

Brak komentarzy.

Skomentuj

Proszę zalogować się jedną z tych metod aby dodawać swoje komentarze:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s

%d blogerów lubi to: