3. diel - JNI - Prvý príklad Java Native Interface bez IDE
V minulom tutoriále o JNI sme si vytvorili program Hello world niekoľkými spôsobmi pre C, C ++ a Javu. Dnes vytvoríme prvú JNI aplikáciu. Nevyužijeme zatiaľ žiadne IDE a všetko vytvoríme len cez príkazový riadok. Možno zistíte, že cez CMD vám to bude pripadať jednoduchšie a ľahšie, než v ďalších dieloch cez IDE. Tento diel má prakticky objasniť iba princíp riešenia. Tu navrhnem postup, ktorý je nutné splniť krok za krokom. Vyzerá to síce zložito, ale zas taký horor to nie je. V najbližších dielach, kedy budeme využívať IDE, zistíte, že niektoré kroky s IDE sú zložitejšie. Vďaka IDE však máte oveľa väčší prehľad v projekte a vytvárať rozsiahlejšie natívne knižnice bez IDE je prakticky prejav šialenstvo.
Postup
Budeme postupovať podľa tejto osnovy do verzie vrátane Java9:
- Vytvoríme Java projekt vrátane spúšťací triedy (* .java)
- Túto triedu skompilujeme, získame (* .class)
- Z kompilované triedy vytvoríme header file (hlavičkový súbor) pre C / C ++ (* .h)
- Z header filu (hlavičkového súboru) vytvoríme source file (zdrojový súbor * .c)
- Daný zdrojový súbor skompilujeme a nalinkuje, vznikne * .dll súbor
- Upravíme spúšťacie Java triedu, aby sme načítali natívne knižnicu
- Zavoláme natívne metódy z Javy
Budeme postupovať podľa tejto osnovy od verzie Java9:
- Vytvoríme Java projekt vrátane spúšťací triedy (* .java)
- Túto triedu skompilujeme, získame (.class) a (hlavičkový súbor) pre C / C ++ (.h)
- Z header filu (hlavičkového súboru) vytvoríme source file (zdrojový súbor * .c)
- Daný zdrojový súbor skompilujeme a nalinkuje, vznikne * .dll súbor
- Upravíme spúšťacie Java triedu, aby sme načítali natívne knižnicu
- Zavoláme natívne metódy z Javy
1 - Tvorba projektu a Java triedy
Ako prvý samozrejme spustíme príkazový riadok (command prompt). vytvoríme adresár
md ProgramJNI
a presunieme sa do neho
cd ProgramJNI
Ďalej vytvoríme súbor
fsutil file createnew programJava.java 0
ktorý bude slúžiť na uloženie zdrojového kódu v Jave. Ďalej si cez Notepad otvoríme súbor
notepad ProgramJava.java
a vložíme (napíšeme) kód, ktorý je nutný pre úspešnú kompiláciu a spustenie programu. Súbor uložíme a Notepad ukončíme.
public class ProgramJavaJNI { native void volameMetodu(); native void posliInt(int cislo); public static void main (String [] args) { System.out.println("Testovací Vypis Java"); } }
Teraz si trochu preberme čo sme vlastne napísali:
- Upozorňujem, že trieda nie je umiestnená v balíčku, s využitím balíka je riešenie trochu komplikovanejšia a nie je to predmetom tohto dielu. Môžete si to sami v rámci vlastnej iniciatívy vyskúšať, ale upozorňujem, že vám to asi na prvýkrát nepobeží.
- Tu pravdepodobne prvýkrát vidíte praktické využitie rezervovaného slovíčka "native". Áno, tu to je, to je presne to správne použitie. Ide o špecifikáciu metód pre JNI. Akákoľvek metóda označená "native" hovorí pri volaní JVM kde ju má hľadať.
- Názov natívnych metód pre MinGW je nutné pomenovať vždy s malým písmenkom. Kompilácia / linkovanie vám prebehne, ale JVM vám pri zavolaní vyvolá exception. Tak to teda v Java SE7 ešte fungovalo.
- Čo danej metódy vlastne robia? Prvá metóda nemá návratový typ. Druhá metóda je opäť void, ale do natívne časti kódu posielame číselnú hodnotu. Ide o celé číslo typu integer. Prakticky vykonáme za pomocou JNI len výpis do konzoly, nič zložitejšieho pre začiatok nie je nutné.
2 - Kompilácia Java triedy
Vykonáme skompilovaniu programu a potom jeho testovacie spustenie. Pretože sme nastavili Javu do systémových premenných ako PATH, je nám umožnené volať akýkoľvek príkaz v akomkoľvek adresári. Za pomocou príkazu
javac ProgramJavaJNI.java
vykonáme kompiláciu. Vysledkom bude súbor ProgramJavaJNI.class. Tento súbor je možné spustiť príkazom
java ProgramJavaJNI
Od verzie Java9 kompilácie už nie je nutná. Vlastnosti
tretieho kroku príkazu javah
prevzal javac
. Takže
teraz stačí vykonať tento príklad
javac -h . ProgramJavaJNI.java
a krok 3 možno preskočiť. Týmto
príkazom získame skompilovaný súbor a priamo aj header file.
javac -h . ProgramJavaJNI.java
3 - Tvorba hlavičkového súboru C / C ++
Teraz vygenerujeme header file (hlavičkový súbor * .h), ktorý využijeme pre zdrojový súbor. To prevedieme príkazom javah -jni ProgramJavaJNI. Toto nám vygeneruje súbor ProgramJavaJNI.h ktorý si za pomocou príkazu môžeme prezrieť. Aby sme to nemuseli prepisovať otvoríme si ho v notepadu ale NESMIETE ho editovať a ukladať do neho. Nejaký podrobnejší opis daného hlavičkového súboru bude až v najbližších kapitolách.
Od verzie Java10 bol príkaz javah
odstránený. K
získaniu header file je nutné použiť príkaz javac
, viď.
poznámka bod 2.
4 - Tvorba zdrojového súboru C / C ++
Teraz si vytvoríme nový súbor ProgramJavaJNI.c, ktorý
bude slúžiť ako zdrojový súbor. To opäť docielime príkazom
fsutil file createnew ProgramJava.c 0 a tento súbor opätovne
otvoríme v notepadu.
Telá metód z hlavičkového súboru skopírujeme a doplníme vnútro metód
(funkcií). Ako som vyššie spomenul, bude sa jednať iba o vypisovať funkcie,
takže v C voláme len funkciu printf. Nič zložitejšieho v
tomto článku riešiť nebudeme. Prvá metóda nevykonáva nič zložité iba
výpis a triviálne súčet a výpis daneho súčtu.
#include <jni.h> #include <stdio.h> #include "ProgramJavaJNI.h" JNIEXPORT void JNICALL Java_ProgramJavaJNI_volameMetodu(JNIEnv *env, jobject obj){ printf("Zde je vystup pro zavolanou nativni metodou\n"); int a=10; int b=25; int c=a+b; printf("Soucet je : %d \n",c); } JNIEXPORT void JNICALL Java_ProgramJavaJNI_posliInt(JNIEnv *env, jobject obj, jint cislo){ printf("Zde je vystup pro zavolanou nativni metodou : %d \n",cislo); }
Ďalej je nutné doplniť identifikátory parametrov do parametrov metód (funkciu). Taktiež teraz nebudeme rozoberať includované súbory.
5 - Kompilácia C / C ++ súboru
Tu prichádza trochu komplikovanejšia časť. Počítam že ste podľa kap.1 skopírovali jni.ha jni_md.h do definovaného adresára. Ak áno príkaz bude výrazne jednoduchšie. Ako prvý postup predvediem kompilácii a build s nakopírovanými hlavičkovými súbormi od JNI. Ak používate kompilátor podporujúci 32bit / 64bit nemal by vzniknúť absolútne žiadny problém. Problém by mohol vzniknúť ak máte iba 32bit kompilátor a snažíte sa vykonať build na 64bit OS. Buildování by sa vykonalo ale JVM v 7 kroku by vám vyhodil výnimku o problémoch načítanie 32bit knižnice v 64bit JVM.
Zadáme tento príkaz na kompilácii: x86_64-w64-mingw32-gcc -c
ProgramJavaJNI.c -o ProgramJavaJNI.o
a vytvoríme ProgramJavaJNI.o Potom zadáme tento príkaz pre
linkovanie: x86_64-w64-mingw32-gcc -Wl, - add-stdcall-alias -shared -o
knihovna.dll ProgramJavaJNI.o
a vytvoríme súbor knihovna.dll
Ak by sme nevykonali nakopírovaniu jni.ha jni_md.h do definovaných adresárov podľa kapitoly 1 príkaz pre kompiláciu by vyzeral takto. Samozrejme cestu k adresárom v Jave si upravte podľa vlastnej inštalácie. x86_64-w64-mingw32-gcc -I "C: \ Program Files \ JavaJDK \ include" -I "C: \ Program Files \ JavaJDK \ include \ win32" -c ProgramJavaJNI.c -o ProgramJavaJNI.o
6 - Úprava Java triedy
Opätovne sa vrátime do zdrojového kódu Java súboru. Tu vykonáme úpravy. Prvý zásadný úprava bude zavedenie knižnice a druhá vyvolanie ich natívnych metód. Prakticky tu nie je čo pokaziť. Súbor zdrojového kódu samozrejme potom uložíme.
public class ProgramJavaJNI { static { try { System.loadLibrary("knihovna"); System.out.println("Nactena knihovna knihovna.dll"); } catch(UnsatisfiedLinkError e){ System.err.println("Nelze nacist knihovnu knihovna.dll"); System.err.println(e.getMessage()); } } native void volameMetodu(); native void posliInt(int cislo); public static void main (String [] args) { System.out.println("Testovací Vypis Java"); ProgramJavaJNI program = new ProgramJavaJNI(); program.volameMetodu(); program.posliInt(99); } }
Načítanie knižnice vykonávam v statickom bloku, tým docielim toho, že sa načíta ako úplne prvý pri vzniku objektu, teda ešte pred vznikom konstruktoru. Pretože všetko je umiestnené v jednom adresári a Java trieda je bez balíčka, JVM všetko ľahko načíta a spustí bez dodatočných parametrov.
7 - Volanie natívnych metód z Javy
Konečne prichádza finále, kedy si vyskúšame prvý program využívajúci JNI. Samozrejme je nutné triedu najskôr skompilovať známym príkazom "javac" a potom program spustíme príkazom "java".
Pripomínam, že * .dll knižnice ako produkt natívneho jazyka zdedili slabiny natívnych jazykov a to je napr. Nekompatibilita medzi rôznymi operačnými systémami a tiež aj medzi rôznymi procesormi. Tak aby ste neboli nemilo prekvapení. Vyššie uvedený príklad, ktorý je prílohou, je knižnica pre 64bit Windows, zbuildovaná pod procesorom Intel. Na 32bit Windows nefunguje.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 16x (15.89 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java