4. díl - JNI - Příklad v Eclipse s makefile

Java Pro pokročilé JNI JNI - Příklad v Eclipse s makefile

V minulém tutoriálu o práci s Java Native Interface 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

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

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

Výběr konverze

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

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

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

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

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) $*


Makefile

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

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

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


Header File

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

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.

# 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


Nový Makefile

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

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

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

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

Vystup Java Native Interface aplikace v Eclipse

V příštím díle si konečně vytvoříme JNI projekt přes Eclipse IDE bez makefilu.


 

Stáhnout

Staženo 70x (19.82 kB)

 

  Aktivity (3)

Článek pro vás napsal Robert Michalovič
Avatar
viz. linkedin

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 



 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!