Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.

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:

  1. Vytvoríme Java projekt vrátane spúšťací triedy (* .java)
  2. Túto triedu skompilujeme, získame (* .class)
  3. Z kompilované triedy vytvoríme header file (hlavičkový súbor) pre C / C ++ (* .h)
  4. Z header filu (hlavičkového súboru) vytvoríme source file (zdrojový súbor * .c)
  5. Daný zdrojový súbor skompilujeme a nalinkuje, vznikne * .dll súbor
  6. Upravíme spúšťacie Java triedu, aby sme načítali natívne knižnicu
  7. Zavoláme natívne metódy z Javy

Budeme postupovať podľa tejto osnovy od verzie Java9:

  1. Vytvoríme Java projekt vrátane spúšťací triedy (* .java)
  2. Túto triedu skompilujeme, získame (.class) a (hlavičkový súbor) pre C / C ++ (.h)
  3. Z header filu (hlavičkového súboru) vytvoríme source file (zdrojový súbor * .c)
  4. Daný zdrojový súbor skompilujeme a nalinkuje, vznikne * .dll súbor
  5. Upravíme spúšťacie Java triedu, aby sme načítali natívne knižnicu
  6. 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");
    }
}
Vytvoríme Java triedu - JNI - Java Native Interface

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
Skompilujeme Java program - JNI - Java Native Interface

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.

Tvorba hlavičkového súboru - JNI - Java Native Interface

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.

Vytvorenie zdrojového súboru - JNI - Java Native Interface

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

Buildujeme zdrojáky - JNI - Java Native Interface

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.

Úprava Java triedy - JNI - Java Native Interface

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.

TestCMD - JNI - Java Native Interface

 

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

 

Predchádzajúci článok
JNI - MinGW a Eclipse - CDT príklady
Všetky články v sekcii
JNI - Java Native Interface
Preskočiť článok
(neodporúčame)
JNI - Príklad v Eclipse s Makefile
Článok pre vás napísal Robert Michalovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Programuji převážně v Javě SE,EE a trochu nativním C a CUDA. více viz.https://cz.linkedin.com/in/robert-michalovic
Aktivity