IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 3 - JNI - První příklad Java Native Interface bez IDE

V minulém tutoriálu o JNI, JNI - MinGW a Eclipse - CDT příklady, jsme si vytvořili program Hello world několika způsoby pro C, C++ a Javu.

Dnes vytvoříme první JNI aplikaci. Nevyužijeme zatím žádné IDE a vše vytvoříme pouze přes příkazový řádek. Možná zjistíte, že přes CMD vám to bude připadat jednodušší a snazší, než v dalších dílech přes IDE. Tento díl má prakticky objasnit pouze princip řešení. Zde navrhnu postup, který je nutno splnit krok za krokem. Vypadá to sice složitě, ale zas takový horor to není. V příštích dílech, kdy budeme využívat IDE, zjistíte, že některé kroky s IDE jsou složitější. Díky IDE ovšem máte mnohem větší přehled v projektu a vytvářet rozsáhlejší nativní knihovny bez IDE je prakticky projev šílenství.

Postup

Budeme postupovat podle této osnovy do verze včetně Java9:

  1. Vytvoříme Java projekt včetně spouštěcí třídy (*.java)
  2. Tuto třídu zkompilujeme, získáme (*.class)
  3. Z kompilované třídy vytvoříme header file (hlavičkový soubor) pro C/C++ (*.h)
  4. Z header filu (hlavičkového souboru) vytvoříme source file (zdrojový soubor *.c)
  5. Daný zdrojový soubor zkompilujeme a nalinkujeme, vznikne *.dll soubor
  6. Upravíme spouštěcí Java třídu, abychom načetli nativní knihovnu
  7. Zavoláme nativní metody z Javy

Budeme postupovat podle této osnovy od verze Java9:

  1. Vytvoříme Java projekt včetně spouštěcí třídy (*.java)
  2. Tuto třídu zkompilujeme, získáme (.class) a (hlavičkový soubor) pro C/C++ (.h)
  3. Z header filu (hlavičkového souboru) vytvoříme source file (zdrojový soubor *.c)
  4. Daný zdrojový soubor zkompilujeme a nalinkujeme, vznikne *.dll soubor
  5. Upravíme spouštěcí Java třídu, abychom načetli nativní knihovnu
  6. Zavoláme nativní metody z Javy

1 - Tvorba projektu a Java třídy

Jako první samozřejmě spustíme příkazový řádek (command prompt). Vytvoříme adresář

md ProgramJNI

a přesuneme se do něj

cd ProgramJNI

Dále vytvoříme soubor

fsutil file createnew programJava.java 0

jenž bude sloužit k uložení zdrojového kódu v Javě. Dále si přes Notepad otevřeme soubor

notepad ProgramJava.java

a vložíme (napíšeme) kód, který je nutný pro úspěšnou kompilaci a spuštění programu. Soubor 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");
    }
}
Vytvoříme Java třídu - JNI - Java Native Interface

Nyní si trochu proberme co jsme vlastně napsali:

  • Upozorňuji, že třída není umístěna v balíčku, s využitím balíčku je řešení trochu komplikovanější a není to předmětem tohoto dílu. Můžete si to sami v rámci vlastní iniciativy vyzkoušet, ale upozorňuji, že vám to asi napoprvé nepoběží.
  • Zde nejspíše poprvé vidíte praktické využití rezervovaného slovíčka "native". Ano, tady to je, to je přesně to správné použití. Jedná o specifikaci metod pro JNI. Jakákoliv metoda označená "native" říká při volání JVM kde ji má hledat.
  • Název nativních metod pro MinGW je nutno pojmenovat vždy s malým písmenkem. Kompilace/linkování vám proběhne, ale JVM vám při zavolání vyvolá exception. Tak to tedy v Java SE7 ještě fungovalo.
  • Co dané metody vlastně dělají? První metoda nemá návratový typ. Druhá metoda je opět void, ale do nativní části kódu posíláme číselnou hodnotu. Jedná o celé číslo typu integer. Prakticky provedeme za pomocí JNI pouze výpis do konzole, nic složitějšího pro začátek není nutné.

2 - Kompilace Java třídy

Provedeme zkompilování programu a poté jeho testovací spuštění. Protože jsme nastavili Javu do systémových proměnných jako PATH, je nám umožněno volat jakýkoliv příkaz v jakémkoliv adresáři. Za pomocí příkazu

javac ProgramJavaJNI.java

provedeme kompilaci. Vysledkem bude soubor ProgramJavaJNI­.class. Tento soubor lze spustit příkazem

java ProgramJavaJNI

Od verze Java9 kompilace již není nutná. Vlastnosti třetího kroku příkazu javah převzal javac. Takže nyní stačí provést tento příklad javac -h . ProgramJavaJNI.java a krok 3 lze přeskočit. Tímto příkazem získáme zkompilovaný soubor a přímo i header file.

javac -h . ProgramJavaJNI.java
Zkompilujeme Java program - JNI - Java Native Interface

3 - Tvorba hlavičkového souboru C/C++

Nyní vygenerujeme header file ( hlavičkový soubor *.h ), který využijeme pro zdrojový soubor. To provedeme příkazem javah -jni ProgramJavaJNI. Toto nám vygeneruje soubor ProgramJavaJNI.h který si za pomocí příkazu můžeme prohlédnout. Abychom to nemusely přepisovat otevřeme si jej v notepadu ale NESMÍTE jej editovat a ukládat do něj. Nějaký podrobnější popis daného hlavičkového souboru bude až v příštích kapitolách.

Od verze Java10 byl příkaz javah odstraněn. K získání header file je nutné použit příkaz javac , viz. poznámka bod 2.

Tvorba hlavičkového souboru - JNI - Java Native Interface

4 - Tvorba zdrojového souboru C/C++

Nyní si vytvoříme nový soubor ProgramJavaJNI.c, který bude sloužit jako zdrojový soubor. To opět docílíme příkazem fsutil file createnew ProgramJava.c 0 a tento soubor opětovně otevřeme v notepadu.
Těla metod z hlavičkového souboru zkopírujeme a doplníme vnitřek metod ( funkcí ). Jak jsem výše vzpomenul, bude se jednat pouze o výpisové funkce, takže v C voláme pouze funkci printf. Nic složitejšího v tomto článku řešit nebudeme. První metoda neprovádí nic složitého pouze výpis a trivialní součet a výpis daneho souč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);
}

Dále je nutno doplnit identifikátory parametrů do parametrů metod(funkci). Taktéž nyní nebudeme rozebírat includované soubory.

Vytvoření zdrojového souboru - JNI - Java Native Interface

5 - Kompilace C/C++ souboru

Zde přichází trochu komplikovanější část. Počítám že jste dle kap.1 nakopírovaly jni.h a jni_md.h do definovaného adresáře. Pokud ano příkaz bude výrazně jednodušší. Jako první postup předvedu kompilaci a build s nakopírovanými hlavičkovými soubory od JNI. Pokud používáte kompilátor podporující 32bit/64bit neměl by vzniknout naprosto žádný problém. Problém by mohl vzniknout pokud máte pouze 32bit kompilátor a snažíte se provést build na 64bit OS. Buildování by se provedlo ale JVM v 7 kroku by vám vyhodil vyjímku o problémech načtění 32bit knihovny v 64bit JVM.

Zadáme tento příkaz ke kompilaci : x86_64-w64-mingw32-gcc -c ProgramJavaJNI.c -o ProgramJavaJNI.o
a vytvoříme ProgramJavaJNI.o Pak zadáme tento příkaz pro linkování : x86_64-w64-mingw32-gcc -Wl,--add-stdcall-alias -shared -o knihovna.dll ProgramJavaJNI.o
a vytvoříme soubor knihovna.dll

Pokud bychom neprovedly nakopírování jni.h a jni_md.h do definovaných adresářů dle kapitoly 1 příkaz pro kompilaci by vypadal takto. Samozřejmě cestu k adresářům v Javě si upravte podle vlastní instalace. x86_64-w64-mingw32-gcc -I"C:\Program Files\JavaJDK\in­clude" -I"C:\Program Files\JavaJDK\in­clude\win32" -c ProgramJavaJNI.c -o ProgramJavaJNI.o

Buildujeme Zdroják - JNI - Java Native Interface

6 - Úprava Java třídy

Opětovně se vrátíme do zdrojového kódu Java souboru. Zde provedeme úpravy. První zásadní úprava bude načtení knihovny a druhá vyvolání jejich nativních metod. Prakticky zde není co pokazit. Soubor zdrojového kódu samozřejmě poté 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čtení knihovny provádím ve statickém bloku, tím docílím toho, že se načte jako naprosto první při vzniku objektu, tedy ještě před vznikem konstruktoru. Protože vše je umístěné v jednom adresáři a Java třída je bez balíčku, JVM všechno snadno načte a spustí bez dodatečných parametrů.

Úprava Java třídy - JNI - Java Native Interface

7 - Volání nativních metod z Javy

Konečně přichází finále, kdy si vyzkoušíme první program využívající JNI. Samozřejmě je nutno třídu nejdříve zkompilovat známým příkazem "javac" a poté program spustíme příkazem "java".

Připomínám, že *.dll knihovny jako produkt nativního jazyka zdědily slabiny nativních jazyků a to je např. nekompatibilita mezi různými operačními systémy a také i mezi různými procesory. Tak abyste nebyli nemile překvapeni. Výše uvedený příklad, jenž je přílohou, je knihovna pro 64bit Windows, zbuildovaná pod procesorem Intel. Na 32bit Windows nefunguje.

TestCMD - JNI - Java Native Interface

V příští lekci, JNI - Příklad v Eclipse s makefile, si ukážeme, jak vytvořit JNI aplikaci v IDE Eclipse CDI za využití makefile.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 15x (15.89 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
JNI - MinGW a Eclipse - CDT příklady
Všechny články v sekci
JNI - Java Native Interface
Přeskočit článek
(nedoporučujeme)
JNI - Příklad v Eclipse s makefile
Článek pro vás napsal Robert Michalovič
Avatar
Uživatelské hodnocení:
1 hlasů
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