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 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.

  1. Vytvoříme Java projekt včetně spouštěcí třídy (.java) a zkompilujeme (.class)
  2. Překonvertujeme Java project do C/C++ projektu
  3. Vytvoříme konfigurační soubor buildu makefile a v projektu stvoříme hlavičkový soubor (*.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

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.

Výchozí nastavení Java projektu - JNI - Java Native Interface

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.

Konverze projektu - JNI - Java Native Interface

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

Výběr konverze - JNI - Java Native Interface

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

Nastavení konverze - JNI - Java Native Interface

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.

Zkonvertovaný projekt - JNI - Java Native Interface

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.

Vytvoření adresáře - JNI - Java Native Interface

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".

Vytvoření makefile souboru - JNI - Java Native Interface

(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)

Makefile - JNI - Java Native Interface

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.

Vytvoření target - JNI - Java Native Interface

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\min­gw64\bin. Potvrdíme OK.

Nastavení Buildu - JNI - Java Native Interface

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:

Build projektu - JNI - Java Native Interface

Header File - JNI - Java Native Interface

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;
}


Zdrojový soubor - JNI - Java Native Interface

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/lin­kovaní. 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

Nový Makefile - JNI - Java Native Interface

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.

VytvoreniTargetu - JNI - Java Native Interface

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.

Provedený Build - JNI - Java Native Interface

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));
    }
}


Zdrojový kód Java - JNI - Java Native Interface

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.

Nastavení a spuštění JNI aplikace - JNI - Java Native Interface

Vystup Java Native Interface aplikace v Eclipse - JNI - Java Native Interface

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ínkami

Staženo 497x (19.82 kB)

 

Předchozí článek
JNI - První příklad Java Native Interface bez IDE
Všechny články v sekci
JNI - Java Native Interface
Přeskočit článek
(nedoporučujeme)
JNI - Příklad v Eclipse bez makefile a s řetězcem
Č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