目錄
- 什么是JNI?
- 為什么使用JNI枯饿?
- 怎么使用JNI?
- 在IntelliJ IDEA里使用JNI
<h2 id="1"> 1. 什么是JNI诡必?</h2>
JNI(Java Native Interface) Java本地接口奢方,又叫Java原生接口。它允許Java調(diào)用C/C++的代碼,同時(shí)也允許在C/C++中調(diào)用Java的代碼爸舒◇郑可以把JNI理解為一個(gè)橋梁,連接Java和底層碳抄。其實(shí)根據(jù)字面意思愉老,JNI就是一個(gè)介于Java層和Native層的接口,而Native層就是C/C++層面剖效。
<h2 id="2"> 2. 為什么使用JNI嫉入?</h2>
一般情況下都是從Java的角度來使用JNI,也就是說在Java中調(diào)用C/C++語(yǔ)言來實(shí)現(xiàn)一些操作璧尸。所以從Java角度來說使用JNI具有以下的優(yōu)點(diǎn):
- 能夠重復(fù)使用一些現(xiàn)成的咒林、具有相同功能的的C/C++代碼
- 因?yàn)镃/C++是偏向底層的語(yǔ)言,所以使用C/C++能夠更加的高效爷光,而且也使得Java能夠訪問操作系統(tǒng)中一些底層的特性垫竞。
<h2 id="3"> 3. 怎么使用JNI?</h2>
這里所說的使用JNI是指從Java層調(diào)用C/C++代碼蛀序,一般的使用步驟都是使用Java定義一個(gè)類欢瞪,然后在該類中聲明一個(gè)native的方法,接著使用C/C++來實(shí)現(xiàn)這個(gè)方法的方法體徐裸。
3.1 使用Java聲明native方法
方法一:TestJNI.java
public class TestJNI{
public native void sayHello();
}
在聲明native方法的時(shí)候還可以規(guī)定具體的包遣鼓,例如:
方法二:TestJNI.java
package jnilib;
public class TestJNI{
public native void sayHello();
}
這兩種方式都可以,但是使用這兩種方式聲明native方法重贺,最后生成的動(dòng)態(tài)庫(kù)時(shí)骑祟,在IntelliJ IDEA中的使用方法卻是不一樣(這一點(diǎn)在最后會(huì)進(jìn)行說明),這里我們采用方法二气笙。
3.2 編譯聲明的Java文件
先使用javac
編譯生成.class
文件
javac -d . TestJNI.java
因?yàn)樵谠创a中使用了package的命令次企,所以編譯的時(shí)候需要用"-d ."參數(shù),其中"."表示在當(dāng)前目錄生成 jnilib文件夾來存放編譯生成.class
文件
再使用javah
編譯生成.h
文件
javah jnilib.TestJNI
需要在類文件名前面加上包名潜圃,編譯完成之后缸棵,會(huì)在當(dāng)前目錄生成jnilib_TestJNI.h
的文件,接下來我們用C語(yǔ)言來實(shí)現(xiàn)剛剛聲明的函數(shù)時(shí)谭期,需要include
這個(gè)頭文件堵第。
jnilib_TestJNI.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jnilib_TestJNI */
#ifndef _Included_jnilib_TestJNI
#define _Included_jnilib_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jnilib_TestJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jnilib_TestJNI_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
其中 JNIEXPORT void JNICALL Java_jnilib_TestJNI_sayHello(JNIEnv *, jobject);
就是我們用Java聲明的native函數(shù)經(jīng)過轉(zhuǎn)換之后的形式稚晚,當(dāng)我們用C語(yǔ)言來實(shí)現(xiàn)的時(shí)候需要使用這個(gè)函數(shù)的聲明。
3.3 用C語(yǔ)言來實(shí)現(xiàn)函數(shù)
創(chuàng)建一個(gè)TestJNI.c
文件:
TestJNI.c
#include <stdio.h>
#include "jnilib_TestJNI.h"
JNIEXPORT void JNICALL Java_jnilib_TestJNI_sayHello(JNIEnv *env, jobject object){
printf("Hello World!\n");
}
3.4 生成動(dòng)態(tài)庫(kù)文件
這需要注意的是在不同的操作系統(tǒng)型诚,能夠生成的動(dòng)態(tài)庫(kù)文件也是不一樣的,在Windows中鸳劳,要生成.dll
文件狰贯,在Linux中要生成.so
文件,在Mac OS X中要生成.jnilib
文件赏廓。同時(shí)定義生成的庫(kù)文件名的時(shí)候也要遵循:lib+文件名+擴(kuò)展名 的原則涵紊。本例中我們?cè)贛ac OS X中所以我們定義生成的庫(kù)文件為:libTestJNI.jnilib
。
makefile:
CC=gcc
CFLAGS=I.
libTestJNI.jnilib : TestJNI.c
$(CC) -fPIC -I/Library/java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include -I/Library/java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include/darwin -shared -o $@ $^
執(zhí)行make之后獲得 libTestJNI.jnilib其中/Library/java/JavaVirtualMachines/jdk1.8.0_91.jdk
為Java的安裝目錄幔摸。
3.5 使用生成的動(dòng)態(tài)庫(kù)文件
使用Java調(diào)用生成的動(dòng)態(tài)庫(kù)
Demo.java
import jnilib.TestJNI;
public class Demo{
static{
try{
System.loadLibrary("TestJNI");
}catch(UnsatisfiedLinkError e){
System.err.println("Native code library failed to load.\n" + e);
System.exit(1);
}
}
public static void main(String[] args) {
TestJNI test = new TestJNI();
test.sayHello();
}
}
編譯摸柄、執(zhí)行后得到結(jié)果:
Hello World!
<h2 id="4"> 4. 在IntelliJ IDEA里使用JNI?</h2>
利用IntelliJ IDEA創(chuàng)建項(xiàng)目既忆,這里因?yàn)槲覀冎奥暶鱪ative函數(shù)的時(shí)候使用了package驱负,所以我們需要在src/main/java的目錄下創(chuàng)建一個(gè)文件夾為jnilib,把我們之前生成的TestJNI.java libTest.jnilib
文件放到該目錄下患雇。接著我們創(chuàng)建Demo文件來調(diào)用生成的動(dòng)態(tài)庫(kù)跃脊,但是如果我們此時(shí)運(yùn)行我們的Demo的話會(huì)產(chǎn)生下面的異常:
java.lang.UnsatisfiedLinkError: no GetDownloadID in java.library.path
這時(shí)我們需要點(diǎn)擊EditConfigurations
在 VM Options
一欄填上 -Djava.library.path="/Users/xiangang/JavaWebLearning/DownloadID/src/main/java/jnilib"
雙引號(hào)里面的路徑就是你剛剛創(chuàng)建的 jnilib
文件夾的路徑。
如果我們?cè)诼暶鱪ative函數(shù)的時(shí)候沒有使用package命令苛吱,則我們必須把以上的兩個(gè)文件放在src/main/java目錄下酪术,而且調(diào)用這個(gè)庫(kù)文件的文件也不能使用package。