延續第一篇的教學。示範下列項目的 JNI 實現方式:
-
在原生方法中,呼叫 Java 的方法。
-
原生方法回傳新的參考型別資料。
-
在原生方法中處理參考型別陣列。
-
如何寫入資料到託管陣列(managed array)。
-
由原生方法擲出 Java 例外。
增加 Hello.java 的原生方法
本文在 Hello.java 中新增三個原生方法: put(), concat(), fileGetContent().
blog/rock/Hello.java
產生 C 標頭文件與程式碼骨架
將 hello-glue.h 中的函數原型宣告複製起來,貼到 C 程式碼中 (hello-native.c),再補上函數的參數名稱。透過上述操作,我們將得到最基本的程式碼骨架。
hello-native.c
實作原生方法的內容
接下來,我們就要開始填空,實現原生方法的內容。
put() - invoke method of instance.
實作一個實體方法,接受一個 key 和一個 value ,將它們加入實體的 hash 欄位內。hash 是一個 HashMap ,提供 put 方法。實作時,我們要先透過反射方法向 this 取得 hash 欄位的參考內容,再透過反射方法向 hash 取得 put 方法的參考內容,最後就可以呼叫這個方法。在 java.lang.reflect 亦有相對應的操作。
查詢方法時,需要給予方法描述符號(方法簽名),詳情查看 The Java Native Interface Programmer's Guide and Specification - Method descriptor。此外,雖然 HashMap 是一個泛型類別,但是因為 JVM 執行時已經丟棄了泛型資訊,所以全部的泛型化參數,在 JNI 中都視為 java.lang.Object (jobject) 資料。
In most cases, you do not have to worry about freeing local references when implementing a native method. The Java virtual machine frees them for you when the native method returns to the caller.
The Java Native Interface Programmer's Guide and Specification - 5.2.1 Freeing Local References
concat() - access array of reference type data and return new String.
實作一個靜態方法,接受一個字串陣列,將它們的字串合併成一個新的字串回傳。
字串陣列被視為一個包含參考型別資料的陣列,所以要用 GetObjectArrayElement() 一一取出陣列中的元素內容。複製字串內容時,則應注意 UTF-8 字元與 C 字元的差異,必須使用 GetStringUTFLength() 才能得到以 byte 為計算單位的長度,從而分配足夠的記憶體空間。
fileGetContent() - return a new array or throws exception.
實體一個靜態方法,它將讀取指定的檔案內容,將其內容儲存於 byte[] 中回傳。當檔案不存在或是發生 IO 錯誤時,它將會擲出 Java 的例外 (IOException, FileNotFoundException)。
第一個實作版本,先用 C 函數配置未受 Java 託管的陣列(unmanaged array),將檔案的資料內容讀入該未託管空間。再使用 NewByteArray() 配置一個被託管的陣列(managed array),將檔案的資料內容從未託管空間複製到託管空間中。當我們需要寫入資料到託管陣列時,我們要用 GetPrimitiveArrayCritical() 取得指向該託管空間的指標。
擲出例外的方式,請查看 The Java Native Interface Programmer's Guide and Specification - 6. Exceptions
fileGetContent() 第二個實作版本,不將資料先行寫入到 malloc() 配置的未託管空間,而是直接將資料寫進託管空間,節省記憶體用量。
執行
執行結果當如下列所示:
rock@rock-desktop:~/workspace/jni-tutorial$ java -Djava.library.path=. blog.rock.Hello
h.hash.size: 1
h.hash('xyz'): 100
concat: hello-world;石頭成
File size: 13296
Byte[0]: 127
樂多舊網址: http://blog.roodo.com/rocksaying/archives/13196157.html