Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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 5 - JNI - Příklad v Eclipse bez makefile a s řetězcem

V minulém tutoriálu o Java Native Interface, JNI - Příklad v Eclipse s makefile, jsme si ukázali, jak vytvořit JNI aplikaci v IDE Eclipse CDI a použili jsme k tomu makefile.

Dnes si budeme pomáhat trochu i příkazovým řádkem. Ne každý chce totiž v Eclipse používat makefile. Opětovně je projekt téměř identický jako v předchozích dvou dílech. A abychom to trochu okořenili, nebudeme využívat jen primitivní datové typy, ale pohrajeme si i se Stringem.

Osnova postupu

  1. Vytvoříme Java projekt včetně spouštěcí třídy (.java) a zkompilujeme pro Java8, 7, 6, ... (`.class`)
  2. Vytvoříme hlavičkový soubor (*.h) a překonvertujeme do C/C++ projektu
  3. Z header filu (hlavičkového souboru) vytvoříme source file (zdrojový soubor *.c)
  4. Nastavíme kompilátor a linker v Eclipse CDT IDE
  5. Provedeme kompilaci a linkování dané sdílené knihovny (*.dll)
  6. Upravíme spouštěcí Java třídu, abychom načetli nativní knihovnu a zavoláme nativní metody z Javy

1 - Tvorba a kompilace 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 konzole si zkontrolujeme kontrolní výstup. Je to identické z předchozí kapitolou.

public class ProgramString {
    native void metodaTest();
    native String posliString(String str);
    public static void main (String [] args) {
        System.out.println("Testovací výpis z Javy");
    }
}

Co dané metody vlastně dělají? První metoda je jasná (viz. minulé kapitoly). Druhá metoda má návratový typ String, což znamená, že nativní metoda nám přenese hodnotu typu String z nativní části programu do interpretované části programu a zároveň je String i parametr interpretované části, který se přenese do nativní. Pouze provedeme změnu toho řetězce. Prakticky je to triviální část.

Java třída s nativními metodami - JNI - Java Native Interface

2 - Vytvoření hlavičkového souboru a konverze projektu

(pro Java 9,8,7,6,...) Protože nám Eclipse neumožňuje v Project exploreru zobrazit *.class (zkompilované Java třídy) a my potřebujeme vyrobit přes "javah" hlavičkový soubor, tak budeme muset spustit navigator, který nám zobrazí *.class soubory.

Nastavení navigátoru - JNI - Java Native Interface

V navigátoru klikneme na adresář \bin\ a za pomocí klávesové zkratky CTRL+ALT+T se nám v Eclipse zobrazí terminál. Jedná se o obyčejný command prompt, dostupný v Eclipse od verze 3.7. Díky němu vygenerujeme za pomocí příkazu soubor *.h. Přes Popup menu vyvoláme refresh projektu a všimneme si vzniku souboru ProgramString.h (header file *.h) v adresáři \bin\. Tento soubor okamžitě přesuneme do adresáře \src\.

javah -jni ProgramString

Pro Java 10,11[18.9],12[19­.3],...: V adresáři \src\ spustíme terminal a zadáme tento příkaz.

javac -h . ProgramString.java
Vytvoření hlavičkového souboru - JNI - Java Native Interface

Jako další 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, vybereme C/C++ -> zvolíme Convert to a C/C++ Project. Dále nastavíme vlastnosti projektu. Zvolíme projekt a vybereme C Projekt, zvolíme Shared Library (sdílená knihovna) project a typ kompilátoru v našem případě MinGW. Postup je téměř identický jako v předchozím článku.

Změna projektu - JNI - Java Native Interface

Můžeme si všimnout změn Project exploreru v projektu. Projekt se překonvertovat. Samozřejmě automaticky došlo ke změně perspektivy na C/C++. Po konverzi projektu nám vzniknou nějaké errory, ale zatím si jich nevšímejte, brzy se jich zbavíme.

3 - Vytvoření zdrojového souboru C/C++

Nyní si do adresáře \src\, kde je umístěn zdrojový kód Java aplikace a hlavičkový soubor, vytvoříme zdrojový kód pro knihovnu "ProgramString.c". 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ář, kam se má vytvořit, a do File name vyplníme název.

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

Jakmile vytvoříme zdrojový soubor *.c a vložíme tento kód, který uložíme, tak by měly samy zmizet zmíněné errory. Doplníme hlavičky metod (funkcí) deklaracemi a vyplníme těla daných funkcí.

#include <jni.h>
#include <stdio.h>
#include "ProgramString.h"
#include "string.h"
JNIEXPORT void JNICALL Java_ProgramString_metodaTest(JNIEnv *env, jobject obj){
    printf("Testovaci Vypis z Nativni casti JNI Programu");
}

JNIEXPORT jstring JNICALL Java_ProgramString_posliString(JNIEnv *env, jobject obj, jstring str){
    // zpracujeme vstupni retezec
    const char *retezec = (*env)->GetStringUTFChars(env, str, 0);
    printf("Zde je vlozeny retezec : %s\n", retezec);
    //  POZOR pouze 256 znaku
    char veta [256];
    //  vlozeni retezce do pole charu
    strcpy(veta,"Ahoj jak se mas ?");
    // slouceni retezcu v C-cku
    strcat(veta,retezec);
    jstring vystup = (*env)->NewStringUTF(env,veta);
    return vystup;
}

Nyní si trochu probereme co jsme provedli. První funkci kvůli jednoduchosti přeskočíme. Druhá funkce již zajímavější. C-čko samotné nepodporuje String, takže je nutné jej za pomoci JNI metody (funkce) "GetStringUTFChar­s()" převést na pole charů. Zde je seznam všech JNI fukcí podporovaných v JNI API. Syntaxi C nebudu rozebírat. Další funkce "NewStringUTF()" vytváří String, který poté přesuneme do interpretované části, je to návratová hodnota naší funkce.

Vypsání zdrojového kódu - JNI - Java Native Interface

4 - Nastavení kompilátoru a linkeru

Nyní nás čeká nejnudnější, ale také nejdůležitější část. Provedeme nastavení kompilátoru/linkeru v Eclipse IDE. Klikneme (označíme) projekt a stiskneme ALT+ENTER. Ve stromu si vybereme C/C++ Build -> Settings -> a zde v Configuration nastavíme All configurations.

Nastavení projektu - JNI - Java Native Interface

Nyní provedeme nastavení kompilátoru. Ve stromu v záložce Tool Settings vybereme možnost GCC C Compiler a do Commandu vložíme kompilátor pro 64bit x86_64-w64-mingw32-gcc. Zkontrolujeme parametry v All options -O3 -Wall -c -fmessage-length=0. Pravděpodobně v defaultu budete mít – O0, takže se přesuňte do Optimization a nastavte -O3. Jinak pokud bychom nestavili PATH k jni.h a jni_md.h, do PATH bychom zde kompilátoru museli v možnosti Includes (-I) dané paths nastavit.

Nastavení kompilace - JNI - Java Native Interface

Dále provedeme nastavení linkeru. Ve stromu se přepneme na MinGW Linker a do Commandu opět vložíme kompilátor pro 64bit x86_64-w64-mingw32-gcc. Do možnosti Miscellaneous doplníme do Linker flags tento řetězec -Wl,--add-stdcall-alias a poté se zpět přepneme do MinGW C Linker. Zde uvidíme upravený All options. (-shared ... je tam defaultně a označuje tvorbu *.dll).

Nastavení linkeru - JNI - Java Native Interface

Zvolíme Apply a OK. Tím máme nastaven kompilátor i builder.

5 - Kompilace do *.dll

Čeká nás tedy provedení tzv. "buildu". Nejdříve ovšem provedeme vyčištění projektu. V menu zvolíme možnost Project -> Clean ... -> označíme projekt a vypneme automatický build.

Poté klikneme na známé kladívko a build se provede. Sdílená knihovna vznikne v adresáři Debug, odkud ji budeme v Java aplikaci načítat.

Provedení buildu - JNI - Java Native Interface

6 - Úprava Java třídy a volání nativní metody

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 provede načtení knihovny a poté zavoláme nativní metody.

public class ProgramString {
    static {
        try {
            System.loadLibrary("libProjektJNIEclipse");
            System.out.println("Nactena knihovna                                libProjektJNIEclipse.dll");
        }
        catch(UnsatisfiedLinkError e){
            System.err.println("Nelze nacist knihovnu                           libProjektJNIEclipse.dll");
            System.err.println(e.getMessage());
        }
    }
    native void metodaTest();
    native String posliString(String str);
    public static void main(String[] args) {
        System.out.println("Testovaci vypis z JAVY ");
        ProgramString program = new ProgramString();
        program.metodaTest();
        String s = "Isaac Asimov";
        System.out.println("Zde je vystup : " +program.posliString(s));
    }
}


Nastavení Java třídy pro volání native metod - JNI - Java Native Interface

Před samotným spuštěním Java třídy je v IDE nutno opět projekt vyčistit. (Project -> Clean). Opětovně je nutno JVM informovat, kde danou *.dll hledat a toho docílíme nastavením 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, kam vložíme -Djava.library.path=Debug. Poté klikneme na Apply a Run a uvidíme výsledek.

Spuštění JNI projektu - JNI - Java Native Interface

Využívat v kódu *.dll umístěnou v adresáři Debug není moc chytré. Každým spouštěním probíhá kompilace a struktura adresáře Debug se poškodí a přestane fungovat. Proto doporučuji vytvořit nový adresář, např. "dll", a do něj knihovnu nakopírovat. Při spuštění přes IDE je vhodné upravit VM argument -Djava.library.path=dll. Vše by mělo běžet v pořádku. Pokud aplikaci spouštíme z příkazového řádku přes IDE v konzoli nebo přímo přes command prompt, nesmíme zapomenout, že když spustím z adresáře \bin\, v tomto adresáři se adresář \dll\ nenachází a proto je nutné říci JVM ať hledá adresář \dll\ o úroveň výše, přímo v rootu projektu.

Testovací výpis - JNI - Java Native Interface

To je pro zatím vše, základní kostra řešení byla ukázána a jemné detaily různých datových typů, objektů, pointer vs. reference, vláken, apod., to již bude na vás.

V příští lekci, JNI - Příklad v Eclipse s C++, si vyzkoušíme pokročilejší příklad s řetězci a C++.


 

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 9x (74 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
JNI - Příklad v Eclipse s makefile
Všechny články v sekci
JNI - Java Native Interface
Přeskočit článek
(nedoporučujeme)
JNI - Příklad v Eclipse s C++
Článek pro vás napsal Robert Michalovič
Avatar
Uživatelské hodnocení:
Ještě nikdo nehodnotil, buď první!
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