上一篇介紹了Linux
平臺的JNI
編程方法,Windows
平臺的JNI
本地調用基本類似,區別就是製作的動態庫不同,Linux
平臺是*.so
,Windows
平臺是*.dll
。其中,Windows
平臺的函數庫也分爲靜態庫和動態庫,下面介紹一下相關概念:
靜態庫
在鏈接步驟中,連接器將從庫文件取得所需的代碼,複製到生成的可執行文件中,這種庫稱爲靜態庫。其特點是可執行文件中包含了庫代碼的一份完整拷貝;缺點就是被多次使用就會有多份冗餘拷貝。即靜態庫中的指令都全部被直接包含在最終生成的EXE
文件中了。在vs
中新建生成靜態庫的工程,編譯生成成功後,只產生一個*.lib
文件
動態庫
動態鏈接庫是一個包含可由多個程序同時使用的代碼和數據的庫,DLL
不是可執行文件。動態鏈接提供了一種方法,使進程可以調用不屬於其可執行代碼的函數。函數的可執行代碼位於一個DLL
中,該DLL
包含一個或多個已被編譯、鏈接並與使用它們的進程分開存儲的函數。在vs
中新建生成動態庫的工程,編譯成功後,產生一個.lib
文件和一個.dll
文件
那麼靜態庫中的lib
和動態庫中的lib
究竟有什麼區別呢?
靜態庫中的lib:該lib
包含函數代碼本身(即包括函數的索引,也包括實現),在編譯時直接將代碼加入程序當中
動態庫中的lib:該lib
包含了函數所在的dll
文件和文件中函數位置的信息(索引),函數實現代碼由運行時加載在進程空間中的dll
提供
本文製作動態庫,即生成*.lib
和*.dll
文件,使用Visual Studio 2017
工具,步驟和linux
平臺的類似,下面詳細介紹
製作動態庫
1.編寫native聲明方法的java類
編寫Java
類,聲明一個native
的本地方法
public class Hello {
public native static String sayHello(String name);
static {
System.load("E:\\Eclipse\\Hello\\libhello.dll");
}
public static void main(String[] args) {
Hello hello = new Hello();
String ret = hello.sayHello("kelvin");
System.out.println(ret);
}
}
2.編譯java類
使用javac
進行編譯
#javac Hello.java
3.生成本地文件*.h
使用javah
生成Hello.h
頭文件,依賴上一步的Hello.class
# javah -jni Hello
4.使用Visual Studio 2017創建構建動態庫
接下來,使用Visual Studio 2017
製作動態庫libhello.lib
和libhello.dll
a、新建工程
首先,新建工程,文件 -> 新建 -> 項目 -> Visual C++ -> Windows 桌面 -> 動態鏈接庫(DLL)
b、添加頭文件
把生成的本地頭文件Hello.h
導入到工程中;另外,還需要把jni.h
和jni_md.h
這兩個頭文件也導入到工程中
c、編寫本地方法的實現
新增Hello.cpp
文件,編碼實現本地方法
#include "stdafx.h"
#include <iostream>
#include "Hello.h"
using namespace std;
JNIEXPORT jstring JNICALL Java_Hello_sayHello(JNIEnv *env, jclass jc, jstring name)
{
const char *buf = { 0 };
buf = env->GetStringUTFChars(name, NULL);
cout << buf << endl;
return env->NewStringUTF("hello");
}
d、生成動態庫
編譯工程,生成動態鏈接庫。如果是64
位系統,還需要設置Debug
爲x64
,否則會報錯
生成 -> 重新生成解決方案
設置爲64
位系統的動態庫
5、調用動態庫
把工程的根目錄下Debug
中的libhello.dll
拷貝到Hello.class
目錄,如果是x64
則是在目錄x64
目錄下;然後,調用Hello
測試jni
的本地調用
# java -classpath E:\Eclipse\Hello Hello
kelvin
hello
看到輸出,表明了libhello.dll
被正常加載調用了
這就是Windows
的本地調用,雖然Java
程序中提倡單一語言風格,但是本地調用提供了一種解決方案,在Java
語言無法實現時,選擇使用jni
也是一種合適的方式。
參考資料