JNA 之(zhi) 初識(上)
JNA(Java Native Access)框架(jia)(jia)是一個開(kai)源的(de)(de)(de)Java框架(jia)(jia),是SUN公司(si)主導(dao)開(kai)發的(de)(de)(de),建(jian)立(li)在經典(dian)的(de)(de)(de)JNI的(de)(de)(de)基礎之上(shang)(shang)的(de)(de)(de)一個框架(jia)(jia)。使用JNI調(diao)(diao)用共(gong)享類庫(.dll/.so文件)是非(fei)常麻煩(fan)的(de)(de)(de)事情,既需(xu)要編寫java代碼,又(you)要編寫C語言的(de)(de)(de)代理方法,這其中需(xu)要很多數(shu)據類型的(de)(de)(de)轉換,是讓人(ren)非(fei)常頭痛。JNA框架(jia)(jia)就是為(wei)了(le)解決這些問(wen)題(ti)和繁(fan)瑣的(de)(de)(de)事情而開(kai)發的(de)(de)(de),它提供(gong)一組(zu)Java工具類用于在運行期動態訪問(wen)系統本地共(gong)享類庫而不需(xu)要編寫任何Native/JNI代碼。開(kai)發人(ren)員只要在一個java接口(kou)中描述目標native library的(de)(de)(de)函(han)數(shu)與結構,JNA將自動實現Java接口(kou)到native function的(de)(de)(de)映(ying)射,大大降低了(le)Java調(diao)(diao)用本體共(gong)享庫的(de)(de)(de)開(kai)發難度。JNA與.NET平臺上(shang)(shang)的(de)(de)(de)P/Invoke機制一樣簡單和方便。
你只需要下載(zai)一個(ge)jar包(bao),就可以使用(yong)JNA的(de)強大功能(neng)方便地(di)調用(yong)動態(tai)鏈接庫中(zhong)的(de)C函數。下載(zai)地(di)址是:
JNA調用本地的庫函數
假設有一個(ge)動態鏈接庫(ku): CnblogsJna.dll。里面有這樣(yang)一個(ge)函(han)數(shu):
void sayHello(char * name){ printf("C Code Start...\n"); printf("Hello! Mr %s.\n",name); printf("C Code End.\n"); }
此函數接收一個代表姓名的字(zi)符(fu)指針,然后在控制臺上輸出幾句字(zi)符(fu)串。
為了調用這(zhe)個(ge)函(han)數,使用JNA,我們需要編寫下面的JAVA代碼:
1、接口ICnblogsJna.java
import com.sun.jna.Library; import com.sun.jna.Native; /** * @author BCH)王國成 */ public interface ICnblogsJna extends Library { // 接(jie)口實例 ICnblogsJna INSTANCE = (ICnblogsJna) Native.loadLibrary("CnblogsJna",ICnblogsJna.class); // 與(yu)C代碼(ma)映(ying)射的函數 public void sayHello(String name); }
注意:接口(kou)需要繼承制(zhi)JNA的Library接口(kou);
接口內部需要一個公共靜態常量INSTANCE, 通過這個常量,就可以獲得這個接口的實例,從而使用接口的方法。也就是調用動態鏈接庫CnblogsJna.dll中的sayHello函數了。
如果使用JNI,你需要使用System.loadLibrary方法,來加載我們專為JNI編寫的動態鏈接庫,這個動態鏈接庫實際上是我們真正需要的動態鏈接庫的代理。使用JNA是,需要用JNA類庫的Native類的loadLibrary函數,是直接把我們需要的動態鏈接庫載入進來。使用JNA,我們不需要編寫作為代理的動態鏈接庫,不需要編寫一行原生代碼。
Native類(lei)的(de)loadLibrary方(fang)法有兩個參數(shu)(shu):第(di)一個參數(shu)(shu)是.dll或者.so文(wen)件(jian)的(de)名(ming)字,但不帶(dai)后綴名(ming)。這符合JNI的(de)規范(fan),因為(wei)帶(dai)了(le)后綴名(ming)就不可以跨操作(zuo)系統(tong)平臺了(le)。第(di)二(er)個參數(shu)(shu)是本(ben)接口(kou)的(de)Class類(lei)型,JNA通過(guo)這個Class類(lei)型,根據指定的(de)dll/.so文(wen)件(jian),動態創建接口(kou)的(de)實(shi)例。
2、調(diao)用動(dong)態鏈接庫文件中函數的Java方(fang)法():
/** * @author BCH)王國成(cheng) */ public class CnblogsJna { /** * 入口函數 * @param args */ public static void main(String[] args) { // 調用動態鏈庫中的sayHello函數 ICnblogsJna.INSTANCE.sayHello("Wanggc/王國成"); } }
方法(fa)很簡單,就像調用(yong)Java自己的(de)函數一(yi)樣。執行輸出(chu)結果如下:

和原生代碼的類型映射
跨平臺(tai),跨語(yu)言(yan)調用(yong)的(de)(de)難點,就是(shi)不(bu)(bu)同語(yu)言(yan)之間數據(ju)類(lei)型不(bu)(bu)一(yi)致造成的(de)(de),JNA也(ye)不(bu)(bu)例(li)外(wai),要想跨平臺(tai)調用(yong),數據(ju)類(lei)型轉換是(shi)個(ge)無法回避的(de)(de)問題。JNA提供了Java和(he)原生代碼的(de)(de)類(lei)型映射。
Java和C數據(ju)類型的對應(ying)表如下:
|
Java 類型 |
C 類型 |
原生表現 |
|
boolean |
int |
32位整(zheng)數 (可(ke)定(ding)制(zhi)) |
|
byte |
char |
8位整數 |
|
char |
wchar_t |
平臺依賴 |
|
short |
short |
16位整數 |
|
int |
int |
32位(wei)整數 |
|
long |
long long, __int64 |
64位整(zheng)數 |
|
float |
float |
32位浮點(dian)數(shu) |
|
double |
double |
64位浮點(dian)數(shu) |
|
Buffer/Pointer |
pointer |
平臺(tai)依賴(lai)(32或 64位指針) |
|
<T>[] (基本類型的數組) |
pointer/array |
32或 64位指(zhi)針(zhen)(參(can)數(shu)/返(fan)回值) 鄰接內存(cun)(結構(gou)體(ti)成員) |
|
String |
char* |
/0結束的數組 (native encoding or jna.encoding) |
|
WString |
wchar_t* |
/0結束的數組(unicode) |
|
String[] |
char** |
/0結束的(de)數(shu)(shu)組的(de)數(shu)(shu)組 |
|
WString[] |
wchar_t** |
/0結束的寬字(zi)符(fu)數(shu)組(zu)的數(shu)組(zu) |
|
Structure |
struct*/struct |
指(zhi)向結構體(ti)(ti)的(de)指(zhi)針 (參數或返回值) (或者明確指(zhi)定是結構體(ti)(ti)指(zhi)針) |
|
Union |
union |
等同于結構體(ti) |
|
Structure[] |
struct[] |
結構體的數(shu)組,鄰(lin)接(jie)內存 |
|
Callback |
<T> (*fp)() |
Java函數指針(zhen)或原(yuan)生函數指針(zhen) |
|
NativeMapped |
varies |
依(yi)賴于定義 |
|
NativeLong |
long |
平臺依賴(lai)(32或64位整(zheng)數) |
|
PointerType |
pointer |
和 Pointer相同(tong) |
由(you)于跨平(ping)臺(tai)和跨語言尤其(qi)自身無法(fa)克服的(de)確定(ding),所以盡量少跨平(ping)臺(tai)、跨語言傳(chuan)遞數(shu)(shu)據(ju)(ju)(ju)。如(ru)果(guo)必(bi)須(xu)這樣做,也盡量使用簡單的(de)數(shu)(shu)據(ju)(ju)(ju)類(lei)型(xing)。如(ru)果(guo)有復雜的(de)數(shu)(shu)據(ju)(ju)(ju)類(lei)型(xing)需要在(zai)(zai)Java和原生(sheng)函(han)數(shu)(shu)中傳(chuan)遞,那么我們就必(bi)須(xu)在(zai)(zai)Java中模(mo)擬這種復雜的(de)原生(sheng)類(lei)型(xing)。這將大(da)(da)大(da)(da)增加實(shi)現的(de)難度,甚(shen)至無法(fa)實(shi)現。如(ru)果(guo)在(zai)(zai)Java和原生(sheng)函(han)數(shu)(shu)間(jian)存(cun)在(zai)(zai)大(da)(da)量的(de)數(shu)(shu)據(ju)(ju)(ju)傳(chuan)遞,一方(fang)面,會(hui)損失程序的(de)性能;另一方(fang)面會(hui)造成內存(cun)碎片(pian),Java調(diao)用原生(sheng)函(han)數(shu)(shu)時,會(hui)把數(shu)(shu)據(ju)(ju)(ju)固定(ding)在(zai)(zai)內存(cun)中,這樣原生(sheng)函(han)數(shu)(shu)才可以訪問這些(xie)Java數(shu)(shu)據(ju)(ju)(ju)。這些(xie)數(shu)(shu)據(ju)(ju)(ju),JVM的(de)GC不能管理,就會(hui)造成內存(cun)碎片(pian)。
歡迎轉載,請注明出處!
感謝您的閱讀,請關注后續博客!
共享視頻教程請訪問:
