Korak po korak: implementacija Java native lokalnih metoda pomoću C jezika
"Sanmej, ranije smo učili o osnovnim metodima u Javi, zapravo Java ima još jednu vrstu metoda — lokalni metod, ili tzv. native metod, koji se prilično razlikuje od prethodnih metoda." Ostavio sam telefon u ruci, okrenuo glavu i rekao Sanmej.
"Zvuči zanimljivo." Sanmej je sa nestrpljenjem čekala.
"Naučiću te da implementiraš native metod pomoću C jezika." nastavio sam. "C jezik je još jedan programski jezik, možeš kliknuti na ovaj link da ga upoznaš i naučiš. Krenimo."
Na primer, private native start0() metod u klasi Thread;
Ili getClass() metod, hashCode() metod, clone() metod u klasi Object.class, čiji su potpisi metoda sledeći:
public final native Class<?> getClass();
public native int hashCode();
protected native Object clone() throws CloneNotSupportedException;Dakle, to su metodi modifikovani ključnom rečju [native], koje u većini slučajeva ne moraju biti implementirani u Java jeziku.
"Erge, zašto uopšte koristimo native za modifikaciju metoda, koja je svrha?" Sanmej je poslušna, ali ovo pitanje je postavila prilično odlučno.
"Dobro, Sanmej, idemo korak po korak."
1. JNI: Java Native Interface
Pre nego što predstavimo native, hajde da prvo razumemo šta je JNI.
U opštem slučaju, možemo potpuno koristiti Java jezik za pisanje programa, ali u određenim situacijama Java možda ne može zadovoljiti potrebe ili ne može bolje zadovoljiti potrebe, na primer:
- ① Standardna Java biblioteka klasa ne podržava određenu funkcionalnost.
- ② Već smo napisali biblioteku klasa u drugom jeziku, recimo C/C++, kako da je pozovemo iz Java koda?
- ③ Za neke metode koji se izvršavaju veoma mnogo puta, da bi se ubrzale performanse, potrebno ih je napisati u jeziku koji je bliži hardveru (npr. asembler).
Ove tri potrebe se, na kraju krajeva, svode na to kako Java kod može pozivati kod napisan u različitim jezicima. Zbog toga je nastao JNI.
Od Jave 1.1, standard Java Native Interface (JNI) postao je deo Java platforme i omogućava Java kodu da interaguje sa kodom napisanim u drugim jezicima.
JNI je prvobitno bio dizajniran za lokalne kompajlirane jezike, posebno C i C++, ali ne sprečava vas da koristite druge jezike, sve dok su konvencije poziva podržane. Korišćenje Jave za interakciju sa lokalnim kompajliranim kodom obično znači gubitak portabilnosti platforme, ali u nekim situacijama je to prihvatljivo, čak i neophodno, na primer pri korišćenju starijih biblioteka, interakciji sa hardverom i operativnim sistemom, ili radi poboljšanja performansi programa. JNI standard barem garantuje da će lokalni kod raditi pod bilo kojom implementacijom Java virtuelne mašine.

Preko JNI-ja, Java program (kod) može pozivati bibliotečne funkcije implementirane tehnikama vezanim za operativni sistem, čime interaguje sa drugim tehnikama i sistemima; istovremeno, druge tehnike i sistemi mogu preko odgovarajućih nativnih interfejsa koje pruža JNI pozivati funkcionalnost implementiranu unutar Java aplikacijskog sistema.
"Erge, sačekaj, Java nije platformno nezavisna? Ako se koristi JNI, onda program ne gubi prednost platformne nezavisnosti?" Moram priznati, Sanmej je ovo pitanje postavila baš na pravo mesto.
"Zaista je tako." Ugasio sam cigaretu između srednjeg i prstenjaka, i nastavio da mirno pričam.
Mane JNI-ja:
- ① Program više nije platformno nezavisan. Da bi se postigla platformna nezavisnost, deo na lokalnom jeziku mora biti ponovo kompajliran u različitim sistemskim okruženjima.
- ② Program više nije apsolutno bezbedan; nepravilno korišćenje lokalnog koda može dovesti do pada celog programa. Opšte pravilo je da lokalne metode treba koncentrisati u mali broj klasa. Na taj način se smanjuje sprega između Jave i C/C++-a.
Trenutno su mane korišćenja JNI-ja u odnosu na prednosti još uvek prihvatljive; moguće je da sa daljim razvojem Java tehnologije nećemo više trebati JNI, ali za sada JDK i dalje pruža podršku za JNI standard.
2. Pisanje lokalnog metoda u C jeziku
"Gore smo objasnili šta je JNI, a sada da napišemo primer: kako pozvati lokalni C program iz Java koda." Okrenuo sam glavu prema Sanmej. "Obrati pažnju 📢."
Zvanična dokumentacija: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html
Koraci su sledeći:
① Pisanje Java klase sa native metodom, generisanje .java fajla;
② Korišćenje komande javac za kompajliranje napisane Java klase, generisanje .class fajla;
③ Korišćenje javah -jni ime_java_klase za generisanje zaglavnog fajla sa ekstenzijom h, odnosno generisanje .h fajla;
④ Implementacija lokalnog metoda pomoću C/C++ (ili drugog programskog jezika), kreiranje implementacije .h fajla, odnosno kreiranje .cpp fajla koji implementira metode iz .h fajla;
⑤ Generisanje dinamičke biblioteke iz fajlova napisanih u C/C++, generisanje dll fajla;
Sada ćemo kroz poziv HelloWorld programa dovršiti ove korake.
01) Pisanje Java klase sa native metodom HelloJNI.java
U direktorijumu /Users/itwanger/Documents/Github/javabetter/testjni kreirajte fajl HelloJNI.java sa sledećim sadržajem.
public class HelloJNI {
static {
System.loadLibrary("hello"); // Učitava dinamičku biblioteku pod imenom libhello.dylib
}
// Definiše lokalni metod
private native void helloJNI();
public static void main(String[] args) {
new HelloJNI().helloJNI(); // Poziva lokalni metod
}
}PS: komande koje se kasnije izvršavaju biće u direktorijumu testjni.
Da objasnim ovaj kod:
private native void helloJNI(): metod deklarisan sa native govori JVM-u da je implementacija tog metoda definisana spolja, odnosno da ćemo ga implementirati u C jeziku.
System.loadLibrary("hello"): učitava dinamičku biblioteku, parametar hello je ime dinamičke biblioteke. Možemo ovo razumeti ovako: metod helloJNI() u programu nije implementiran, ali ga želimo pozvati — šta da radimo?
Taj metod moramo inicijalizovati, pa smo koristili static blok za inicijalizaciju, o čemu će biti reči kasnije.
02) Kompajliranje HelloJNI.java
U komandnoj liniji, izvršite javac HelloJNI.java da kompajlirate izvorni kod.

03) Korišćenje javah -jni HelloJNI za generisanje zaglavnog fajla sa ekstenzijom h

PS: Od Jave 9 nadalje, javah je napušten, a zamenjen je korišćenjem opcije -h za generisanje zaglavnog fajla, na primer
javac -h . ImeKlase.java.
Nakon izvršenja, u direktorijumu gde se nalazi HelloJNI.java generisaće se zaglavni fajl pod imenom HelloJNI.h. Otvorite fajl HelloJNI.h i videćete sledeći kod.

Nije problem ako ne razumete, sve dok znate da je automatski generisan.
04) Implementacija lokalnog metoda u C jeziku
Kreirajte C fajl HelloJNI.c koji implementira lokalni metod sayHello.
#include <stdio.h>
#include <jni.h>
#include "HelloJNI.h"
JNIEXPORT void JNICALL Java_HelloJNI_helloJNI(JNIEnv *env, jobject obj) {
printf("Hello, JNI!\n");
return;
}Imajte na umu da ovde treba uključiti JNI zaglavni fajl i da ime implementiranog metoda mora biti konzistentno sa imenom deklarisanim u Javi (metod helloJNI klase HelloJNI → HelloJNI_helloJNI).
05) Pisanje skripte za kompajliranje compile.sh
#!/bin/bash
# Kompajlira HelloJNI.c fajl
gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" -shared -o libhello.dylib HelloJNI.c
# Kopira generisani fajl libhello.dylib u trenutni direktorijum
cp libhello.dylib .Stvari na koje treba obratiti pažnju:
$JAVA_HOMEje putanja instalacije JDK-a i mora se izmeniti prema stvarnoj situaciji.- Na macOS-u, ekstenzija dinamičke biblioteke (hello) je .dylib, a ne .so kao na Linuxu.
Opciona -I ovde služi da kaže kompajleru gde se nalaze zaglavni fajlovi, a $JAVA_HOME je putanja do direktorijuma gde je instalirana Java.
06) Izvršavanje skripte za kompajliranje
sh compile.shNakon izvršenja, u trenutnom direktorijumu biće generisana dinamička biblioteka pod imenom libhello.dylib.

07) Pokretanje HelloJNI
Izvršite komandu java HelloJNI da pokrenete HelloJNI; ako sve prođe normalno, na terminalu će biti ispisan Hello, JNI!.

3. Dijagram toka JNI poziva C

4. native ključna reč
"Sanmej, da li sada već znaš šta je native?" upitao sam Sanmej.
"Hmm, da pokušam ukratko, a ti, Erge, vidi da li dobro kažem."
native se koristi za modifikaciju metoda; metod deklarisan sa native znači da je implementacija tog metoda definisana spolja i može biti implementirana u bilo kom jeziku, na primer C/C++. Jednostavno rečeno, native Method je interfejs preko kojeg Java poziva ne-Java kod.
native sintaksa:
- ① Pozicija modifikacije metoda mora biti pre tipa povratne vrednosti; odnos prema ostalim kontrolnim modifikatorima metoda nije ograničen.
- ② Ne može se koristiti abstract modifikator, nema telo metoda i nema vitičaste zagrade.
- ③ Tip povratne vrednosti može biti bilo koji tip.
"Sanmej, dobro ti ide." Bio sam veoma zadovoljan njenom sposobnošću učenja. "U svakodnevnom programiranju, kada vidimo native modifikovan metod, dovoljno je da znamo koja je svrha tog metoda; ostalo ne moramo da brinemo, operativni sistem će ga implementirati za nas, a u početnoj fazi učenja ne treba ulaziti previše duboko."
Tutorial za instalaciju gcc-a na Windowsu: http://blog.csdn.net/altland/article/details/63252757 native referentni link: https://www.zhihu.com/question/28001771/answer/2049534464
