1.JNA簡介java
JNA(Java Native Access)框架是一個開源的Java 框架,是SUN 公司主導開發的,創建在經典的JNI 的基礎之上的一個框架。JNA 項目地址:https://jna.dev.java.net/。面試
JNI 是Java 調用原生函數惟一的機制。JNA 也是創建在JNI 技術之上的,它簡化了Java調用原生函數的過程。使用JNA可使你像調用java方法同樣直接調用本地方法,極大地擴展了java平臺的整合能力。編程
JNA 提供了一個動態的C 語言編寫的轉發器,能夠自動實現Java 和C 的數據類型映射。windows
2.JNA調用原生函數示例數組
假設libCms.dll動態連接庫中發佈了以下C函數:app
lc_init(string filename,Param param)
該函數做用是根據監聽參數啓動一個監聽,CmsListenParam具體是什麼暫時不用管,後面會詳細介紹。爲了調用這個原生函數,使用JNA,編寫以下java代碼:框架
private interface GetBroadcastIds extends Library { GetBroadcastIds INSTANCE = (GetBroadcastIds) Native.loadLibrary("LCAudioThrDll",GetBroadcastIds.class); int lc_init(String filename,Param param); }
而後咱們就能夠像調用java代碼同樣調用原生函數了:編程語言
public static void main(String[] args) { String filename = "D:\\KuGou\\體面.mp3"; Param param = new Param(); param.sethWnd(0L);//Windows窗口句柄,若是不爲NULL,線程將事件消息發送到此窗口 param.setPriority(0);//音頻流優先級0~255 param.setCastMode(2);//播放方式,單播,組播和廣播[0,1,2] param.setVolume(80);//音量0~100 param.setIP(3232261139L);//IP轉數字 param.setTone(0);//音調(未使用)0~15 param.setBass(200);//低音頻率0~3000 param.setTreble(3000);//高音頻率200~20000 param.setTrebleEn(0);//高音放大因子0~255 param.setBassEn(0);//低音放大因子0~255 param.setSourcType(0);//音頻數據源,0表示數據源爲文件,1表示數據源爲聲卡輸入[0,1,2,3] DDMineService.instance.lc_init(filename,param); System.out.println("調用成功"); }
3.JNA調用原生函數的模式函數
JNA中使用Native關鍵字來聲明一個Java方法表明外部的原生函數,JNA不使用Native表明原生函數,而是使用java interface來表明動態連接庫中表明的全部原生函數,對於不使用的原生函數,能夠不在interface中聲明原型。 性能
4.java和原生代碼的類型映射
JNA 使用的數據類型是Java 的數據類型。而原生函數中使用的數據類型是原生函數的編程語言使用的數據類型。多是C,Delphi,彙編等語言的數據類型。如數據類型映射不一致,在調用中可能會發生沒法預知的行爲,多是調用不成功,也可能不能正確解析返回數據。
C#,java和操做系統數據類型對應表
C,java和操做系統數據類型對應表
native type | size | java type | common windows types |
char | 8-bit integer |
byte | BYTE, TCHAR |
short | 16-bit integer | short | WORD |
wchar_t | 16/32-bit character | char | TCHAR |
int | 32-bit integer | int | DWORD |
int | boolean value | boolean | BOOL |
long | 32/64-bit integer | NativeLong | LONG |
long long | 64-bit integer | long | __int64 |
float | 32-bit FP | float | |
double | 64-bit FP | double | |
char* | C string | String | LPTCSTR |
void* | pointer | Pointer | LPVOID, HANDLE, LPXXX |
pointer | Buffer/Pointer | 平臺依賴(32 或64 位指針) | |
pointer/array | <T>[] (基本類型的數組) | 32 或64 位指針(參數/返回值) 鄰接內存(結構體成員) |
|
wchar_t* | WString | \0 結束的數組(unicode) | |
char** | String[] | \0 結束的數組的數組 | |
wchar_t** | WString[] | \0 結束的寬字符數組的數組 | |
struct*/struct | Structure | 指向結構體的指針(參數或返回值) (或者明確指定是結構體指針)/結構體(結構體的成員) (或者明確指定是結構體) | |
union | Union | 等同於結構體 | |
Structure[] | struct[] | 結構體的數組,鄰接內存 | |
<T> (*fp)() | Callback | Java 函數指針或原生函數指針 | |
varies | NativeMapped | 依賴於定義 | |
pointer | PointerType | 和Pointer 相同 |
5.跨平臺、跨語言調用原則:
儘可能使用基本、簡單的數據類型;
儘可能少跨平臺、跨語言傳遞數據!
若是有複雜的數據類型須要在Java 和原生函數中傳遞,那麼咱們就必須在Java 中模擬
大量複雜的原生類型。這將大大增長實現的難度,甚至沒法實現。
若是在Java 和原生函數間存在大量的數據傳遞,那麼一方面,性能會有很大的損失。
更爲重要的是,Java 調用原生函數時,會把數據固定在內存中,這樣原生函數才能夠訪問這
些Java 數據。這些數據,JVM 的GC 不能管理,會形成內存碎片。
若是在你須要調用的動態連接庫中,有複雜的數據類型和龐大的跨平臺數據傳遞。那麼
你應該另外寫一些原生函數,把須要傳遞的數據類型簡化,把須要傳遞的數據量簡化。