JNA 之 初識(上)


      JNA(Java Native Access)框架是一個開源的Java框架,是SUN公司主導開發的,建立在經典的JNI的基礎之上的一個框架。使用JNI調用共享類庫(.dll/.so文件)是非常麻煩的事情,既需要編寫java代碼,又要編寫C語言的代理方法,這其中需要很多數據類型的轉換,是讓人非常頭痛。JNA框架就是為了解決這些問題和繁瑣的事情而開發的,它提供一組Java工具類用於在運行期動態訪問系統本地共享類庫而不需要編寫任何Native/JNI代碼。開發人員只要在一個java接口中描述目標native library的函數與結構,JNA將自動實現Java接口到native function的映射,大大降低了Java調用本體共享庫的開發難度。JNA與.NET平台上的P/Invoke機制一樣簡單和方便。

      你只需要下載一個jar包,就可以使用JNA的強大功能方便地調用動態鏈接庫中的C函數。下載地址是:https://github.com/twall/jna

      JNA調用本地的庫函數

    假設有一個動態鏈接庫: CnblogsJna.dll。里面有這樣一個函數:

void sayHello(char * name){
    printf("C Code Start...\n");
    printf("Hello! Mr %s.\n",name);
    printf("C Code End.\n");
}

      此函數接收一個代表姓名的字符指針,然后在控制台上輸出幾句字符串。

      為了調用這個函數,使用JNA,我們需要編寫下面的JAVA代碼:

      1、接口ICnblogsJna.java

import com.sun.jna.Library;
import com.sun.jna.Native;

/**
 * @author BCH)王國成
 */
public interface ICnblogsJna extends Library {
    // 接口實例
    ICnblogsJna INSTANCE = (ICnblogsJna) Native.loadLibrary("CnblogsJna",ICnblogsJna.class);

    // 與C代碼映射的函數
    public void sayHello(String name);
}

      注意:接口需要繼承制JNA的Library接口;

             接口內部需要一個公共靜態常量INSTANCE, 通過這個常量,就可以獲得這個接口的實例,從而使用接口的方法。也就是調用動態鏈接庫CnblogsJna.dll中的sayHello函數了。
            如果使用JNI,你需要使用System.loadLibrary方法,來加載我們專為JNI編寫的動態鏈接庫,這個動態鏈接庫實際上是我們真正需要的動態鏈接庫的代理。使用JNA是,需要用JNA類庫的Native類的loadLibrary函數,是直接把我們需要的動態鏈接庫載入進來。使用JNA,我們不需要編寫作為代理的動態鏈接庫,不需要編寫一行原生代碼。
            Native類的loadLibrary方法有兩個參數:第一個參數是.dll或者.so文件的名字,但不帶后綴名。這符合JNI的規范,因為帶了后綴名就不可以跨操作系統平台了。第二個參數是本接口的Class類型,JNA通過這個Class類型,根據指定的dll/.so文件,動態創建接口的實例。

      2、調用動態鏈接庫文件中函數的Java方法():

/**
 * @author BCH)王國成
 */
public class CnblogsJna {
    /**
     * 入口函數
     * @param args
     */
    public static void main(String[] args) {
        // 調用動態鏈庫中的sayHello函數
        ICnblogsJna.INSTANCE.sayHello("Wanggc/王國成");
    }
}

      方法很簡單,就像調用Java自己的函數一樣。執行輸出結果如下:

     

      和原生代碼的類型映射

      跨平台,跨語言調用的難點,就是不同語言之間數據類型不一致造成的,JNA也不例外,要想跨平台調用,數據類型轉換是個無法回避的問題。JNA提供了Java和原生代碼的類型映射。

      Java和C數據類型的對應表如下:

Java

C

原生表

 boolean

 int

 32位整數 (可定制)

 byte

 char 

 8位整數

 char

 wchar_t

 平台依賴

 short

 short

 16位整數

 int

 int

 32位整數

 long

long long, __int64

 64位整數

 float

 float

 32位浮點數

 double

 double

 64位浮點數

 Buffer/Pointer

 pointer

 平台依賴(32或 64位指針)

 <T>[] (基本類型的數組)

 pointer/array

32或 64位指針(參數/返回值)

鄰接內存(結構體成員)

 String

 char*

/0結束的數組 (native encoding or jna.encoding)

 WString

 wchar_t*

 /0結束的數組(unicode)

 String[]

 char**

 /0結束的數組的數組

 WString[]

 wchar_t**

 /0結束的寬字符數組的數組

 Structure

 struct*/struct

指向結構體的指針 (參數或返回值) (或者明確指定是結構體指針)
結構體(結構體的成員) (或者明確指定是結構體)

 Union

union 

 等同於結構體

 Structure[]

 struct[]

 結構體的數組,鄰接內存

 Callback

 <T> (*fp)()

 Java函數指針或原生函數指針

 NativeMapped

 varies

 依賴於定義

 NativeLong

 long

 平台依賴(32或64位整數)

 PointerType

 pointer

 和 Pointer相同

 

      由於跨平台和跨語言尤其自身無法克服的確定,所以盡量少跨平台、跨語言傳遞數據。如果必須這樣做,也盡量使用簡單的數據類型。如果有復雜的數據類型需要在Java和原生函數中傳遞,那么我們就必須在Java中模擬這種復雜的原生類型。這將大大增加實現的難度,甚至無法實現。如果在Java和原生函數間存在大量的數據傳遞,一方面,會損失程序的性能;另一方面會造成內存碎片,Java調用原生函數時,會把數據固定在內存中,這樣原生函數才可以訪問這些Java數據。這些數據,JVM的GC不能管理,就會造成內存碎片。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM