Lekce 4 - JNI - Příklad v Eclipse s makefile
V minulém tutoriálu o práci s Java Native Interface, JNI - První příklad Java Native Interface bez IDE, jsme si vytvořili první JNI aplikaci. Nepoužili jsme k tomu IDE.
Dnes si ukážeme jak aplikaci vytvořit za pomoci IDE Eclipse s balíčkem CDT. Budeme si pomáhat známým souborem makefile.
Proč toto řešení? Předvede nám jak lze v Eclipse CDT provádět kompilaci/linkování ("tvz. build") dle konfiguračních parametrů. To bývá u C/C++ projektů dost často vítané a požadované. Zajisté víte, že nativní kompilátory umožňují zásadním způsobem ovlivňovat efektivitu z buildovaných programů přes nastavení parametrů. Toho lze úspěšně využít v Eclipse CDT. Tato možnost chybí u interpretovaných jazyků, kde za nás vše řeší VM, které se moc konfigurovat nedají.
Pokud jste si vyzkoušeli předchozí postup, kde jsme vytvářeli *.dll bez IDE, pak možná budete zděšeni přílišnou komplikovaností při práci s IDE a pochopíte, proč někteří programátoři stále odmítají výhody IDE. Ono to skutečně s IDE je složitější, ale získáte tím díky GUI jednoznačně přehled v projektu v navigátoru (project explorer). Další výhodou IDE je značné zjednodušení při vytváření samospustitelného java programu (tvz. Runnable jar). V příkazovém řádku to lze samozřejmě také, ale je to pracnější a složitější (tvorba meta informací pro META-INF), to vše za nás IDE, konkrétně Eclipse, případně v kombinaci s Maven, vyřeší.
Osnova postupu
Prováděný postup je velmi podobný s postupem v předchozím dílu.
- Vytvoříme Java projekt včetně spouštěcí třídy (.java) a zkompilujeme (.class)
- Překonvertujeme Java project do C/C++ projektu
- Vytvoříme konfigurační soubor buildu makefile a v projektu stvoříme hlavičkový soubor (*.h)
- Z header filu (hlavičkového souboru) vytvoříme source file (zdrojový soubor *.c)
- Daný zdrojový soubor zkompilujeme a nalinkujeme, vznikne *.dll soubor
- Upravíme spouštěcí Java třídu, abychom načetli nativní knihovnu
- Zavoláme nativní metody z Javy
1 - Tvorba Java projektu
Jako první krok samozřejmě zkontrolujeme perspektivu. Musí být nastavená Java. Dále vytvoříme Java projekt, který si nějak pojmenujeme. Vytvoříme spouštěcí třídu bez balíčku a doplníme tělo spouštěcí metody + navrhneme nativní metody. Program spustíme a dole v konzoli si zkontrolujeme výstup.
public class ProgramJNI { native void metodaTisk(); native int ziskejInt(); native int zvysIntDeset(int cislo); public static void main (String [] args) { System.out.println("Testovací vypis JNI v Eclipse s makefile"); } }
Co dané metody vlastně dělají? První metoda nemá návratový typ, je typu void a tudíž nic z ní nezískáme do programu Javy. Prakticky opět testovací metoda, kdy kontrolu funkčnosti provedeme přes výstup do konzole. Druhá metoda má návratový typ int, což znamená, že nativní metoda nám přenese hodnotu integer z nativní části programu do interpretované části programu. Třetí metoda nejen, že má jako návratový typ integer, ale má i parametr typu integer. To znamená, že interpretovaná část do nativní části integer pošle a nativní část jej interpretované části vrátí. Česky a matematicky řečeno, pošleme tam číslo a to číslo zvýšíme o 10. Nejedná se o nijak složitou část.

2 - Překonvertování projektu do C/C++
Jako další krok provedeme konverzi z Java Projektu na C/C++ Projekt. K tomu samozřejmě nestačí jen změnit perspektivu. Konverzi provedeme dle obrázku. Označíme projekt, který chceme zkonvertovat, a vyvoláme Popup okno -> new -> other.

Objeví se dialogové okno New a vybereme C/C++. Zvolíme Convert to a C/C++ Project.

Nastavíme vlastnosti projektu. Zvolíme projekt, vybereme C Projekt, zvolíme Makefile project a typ kompilátoru v našem případě MinGW.

Můžeme si všimnout změn Project exploreru v projektu. Projekt se překonvertoval. Samozřejmě automaticky došlo ke změně perspektivy na C/C++. Jen tak mimochodem, stejným způsobem lze v Eclipse konvertovat Java Projekty do struktury Maven.

3 - Tvorba konfiguračního a hlavičkového souboru
Nyní si vytvoříme adresář \jni. Do tohoto adresáře umístíme soubory nutné pro nativní část (header file, source file, makefile, dll). Samozřejmě lze adresář pojmenovat jak se nám zachce, ale je nutno na to brát zřetel a řešení tomu přizpůsobit. File -> New -> Folder -> objeví se dialog, kde vybereme projekt do kterého daný adresář vložíme, a také adresář pojmenujeme "jni" v části Folder name. Pokud vše nastavíme správně, zviditelní se mám tlačítko Finish a my jej potvrdíme.

Do tohoto adresáře vytvoříme makefile (soubor s jménem makefile). Označíme daný adresář a opět vyvoláme Popup okno. Vybereme cestu New -> File -> a objeví se nám opět dialogové okno. Zvolíme adresář, kde se má vytvořit, a do File name vložíme "makefile".

(Java 9,8,7,6,...)Do nově vzniklého souboru vložíme tyto definice:
# Definujeme promenou pro classpath CLASS_PATH = ../bin # Definujeme virtualni cestu pro .class v adresari \bin vpath %.class $(CLASS_PATH) # $* zde nastavujime co vlastne chceme a to ze chceme vytvorit header file ProgramJNI.h : ProgramJNI.class javah -classpath $(CLASS_PATH) $*
Java 10,11[18.9],12[19.3],...: Protože, nám došlo ke změně z
javah -jni ProgramJNI.class
na javac -h . ProgramJNI
,
tak musíme upravit konfigurační soubor makefile tak, aby nám fungoval. Do
nově vzniklého souboru vložíme tyto definice:
# Definujeme promenou pro classpath CLASS_PATH = ../src/ koncovka = .java # Definujeme virtualni cestu pro .java v adresari \src vpath %.java $(CLASS_PATH) # $* zde nastavujime co vlastne chceme a to ze chceme vytvorit header file ProgramJNI.h : ProgramJNI.java javac -h . $(CLASS_PATH)$*$(koncovka)

Nyní nás čeká provedení tvz. "buildu". I když o "build" se v tomto případě nejedná, neboť účelem tohoto nastavení je vytvoření hlavičkového souboru. Nejdříve vytvoříme target (označuje typ buildu), označíme makefile a vyvoláme Popupokno, kde vybereme možnost MakeTargets -> Build.

V dialogovém okně Make Target zvolíme "Add...". Jako Target Name vložíme jméno požadovaného hlavičkového souboru (header file) a hlavně je nutno nastavit Build Command, kde definujeme soubor make v daném kompilátoru. Ten se jmenuje mingw32-make a nachází se v C:\MinGW64\mingw64\bin. Potvrdíme OK.

Nyní jsme si vytvořili náš první makefile, který akorát provede vytvoření ProgramJNI.h, tvz. header file, který potřebujeme pro náš zdrojový soubor.
(Pozn. Java 9,8,7,..) Než spustíme náš makefile, ujistěte se, že
projektu existuje adresář \bin\
, jenž obsahuje námi
zkompilovanou javovskou třídu (tzn. *.class
).
Klikneme na náš projekt v Project exploreru a stiskneme Shift+F9 (nebo v
menu Project -> Make Target -> Build...). Objeví se okno Make Targets a
my zvolíme Build.
Pozn. Někdy se vám může zdát, že bude chybět specifikace Location, v mém
případě jni. I když tam nebude, tak by se build měl provést. Alternativně
můžete manuálně daný adresář upravit v adresáři projektu v souboru
.cproject
, kde na řádku:
<target name="ProgramJNI.h" path="jni" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
doplníte path. To můžete udělat pro jakýkoliv bod buildu.
Po provedení byste měli vidět výpis v konzoli bez warningu a errorů a
taktéž si v Project exploreru v adresáři jni/
všimněte
vzniklého ProgramJNI.h
:


4 - Tvorba zdrojového souboru C/C++
Dále vytvoříme nový soubor "ProgramJNI.c". Vytváří se identickým způsobem jako jsme vytvořili soubor makefile, až na to, že si nevybereme file, ale source file. Nejdříve označíme adresář \jni\, poté klikneme v menu File -> New -> Source File, který bude sloužit jako zdrojový soubor.
V dialogu definujeme jeho jméno a zvolíme Finish. Do daného zdrojového souboru opětovně umístíme hlavičky metod z hlavičkového souboru. Doplníme parametry metod. Přidáme importované knihovny a hlavičky. Nakonec doplníme i těla metod. Pro čtenáře znalé C a Java jsou těla metod triviální.
#include <jni.h> #include <stdio.h> #include "ProgramJNI.h" JNIEXPORT void JNICALL Java_ProgramJNI_metodaTisk(JNIEnv *env, jobject obj){ printf("Zde je vystup pro zavolanou nativni metodou \n"); } JNIEXPORT jint JNICALL Java_ProgramJNI_ziskejInt(JNIEnv *env, jobject obj){ jint hodnota =846; return hodnota; } JNIEXPORT jint JNICALL Java_ProgramJNI_zvysIntDeset(JNIEnv *env, jobject obj , jint cislo){ jint dllCislo = cislo +10; return dllCislo; }

5 - Kompilace do *.dll souboru
Nyní přichází trochu komplikovanější část. Počítám, že jste dle prvního dílu nakopírovali jni.h a jni_md.h do definovaného adresáře. Nyní nás čeká provedení buildu (vytvoření sdílené knihovny) za pomocí makefile. Starý makefile přejmenujeme a vytvoříme nový dle postupu v bodě 3.
Doplníme jej nastavením pro kompilaci/linkovaní. Makefile není předmětem seriálu o JNI a proto si jej zde vysvětlovat nebudeme. Pro Java vývojáře to není zásadní a v příští kapitole předvedu používání IDE Eclipse bez makefilu.
Java 9,8,7,6...:
# Definujeme promenou pro classpath CLASS_PATH = ../bin # Definujeme virtualni cestu pro .class v adresari \bin vpath %.class $(CLASS_PATH) all : ProgramJNI.dll # $@ provadime linkovani ProgramJNI.dll : ProgramJNI.o x86_64-w64-mingw32-gcc -Wl,--add-stdcall-alias -shared -o $@ $< # $@ provadime kompilaci ProgramJNI.o : ProgramJNI.c ProgramJNI.h x86_64-w64-mingw32-gcc -c $< -o $@ # $* ProgramJNI.h : ProgramJNI.class javah -classpath $(CLASS_PATH) $* clean : rm ProgramJNI.o ProgramJNI.dll
Java 10,11[18.9],12[19.3],...:
# Definujeme promenou pro classpath CLASS_PATH = ../src/ koncovka = .java # Definujeme virtualni cestu pro .java v adresari \src vpath %.java $(CLASS_PATH) all : ProgramJNI.dll # $@ provadime linkovani ProgramJNI.dll : ProgramJNI.o x86_64-w64-mingw32-gcc -Wl,--add-stdcall-alias -shared -o $@ $< # $@ provadime kompilaci ProgramJNI.o : ProgramJNI.c ProgramJNI.h x86_64-w64-mingw32-gcc -c $< -o $@ # $* ProgramJNI.h : ProgramJNI.java javac -h . $(CLASS_PATH)$*$(koncovka) clean : rm ProgramJNI.o ProgramJNI.dll

Nyní nás čeká vytvoření a nastavení pro build našeho nového makefilu. Jeho identifikaci jsme v makefilu definovali na "all". Tvorba targetu již byla ukázána, takže opakujeme identický postup (což znamená kliknutí v Project exploreru na makefile a stisknutí SHIFT+F9), a přidáme další (Add...), kde definujeme jméno (Target name) a buildovací příkaz (buildname). Potvrdíme OK.

V Targetech se nám objeví další volba "all". Na ní klikneme (zvýrazníme) a stiskneme tlačítko "Build". Dole v konzole bychom měli vidět provedený úspěšný výpis. V Project exploreru lze vidět vznik nových dvou souborů a ProgramJNI.o a ProgramJNI.dll.

6 - Úprava Java třídy
Po úspěšném zbuildování *.dll knihovny se vrátíme do Javy. Přepneme perspektivu a zobrazíme si zdrojový kód spouštěcí třídy v Javě. Nejdříve provedeme načtení knihovny a poté zavoláme nativní metody.
public class ProgramJNI { static { try { System.loadLibrary("ProgramJNI"); System.out.println("Nactena knihovna ProgramJNI.dll"); } catch(UnsatisfiedLinkError e){ System.err.println("Nelze nacist knihovnu ProgramJNI.dll"); System.err.println(e.getMessage()); } } native void metodaTisk(); native int ziskejInt(); native int zvysIntDeset(int cislo); public static void main(String[] args) { System.out.println("Testovaci vypis JNI v Eclipse s makefile"); ProgramJNI program = new ProgramJNI(); program.metodaTisk(); System.out.println("Zde je vystup Java z nativni dll : "+program.ziskejInt()); System.out.println("Zde je vystup Java z nativni dll se " + "vstupem z Java : "+program.zvysIntDeset(123)); } }

7 - Volání nativní metody z Javy
Konečně přichází finále, kdy si vyzkoušíme první program využívající JNI. Problém je v tom, že daná *.dll je uložená v adresáři \jni\. Je nutno JVM informovat kde danou *.dll hledat a to provedeme v nastavení v konfigurátoru spuštění. Nahoře v menu zvolíme Run -> Run configurations ... a provedeme nastavení. Ve stromu si najdeme spouštěcí třídu přepneme záložku Arguments -> VM argument a vložíme "-Djava.library.path=jni". Poté klikneme na Apply a Run a uvidíme výsledek.


V příštím díle, JNI - Příklad v Eclipse bez makefile a s řetězcem, si konečně vytvoříme JNI projekt přes Eclipse IDE bez makefilu.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkamiStaženo 576x (19.82 kB)