java高級(jí)用法之JNA中使用類型映射
簡介
JNA中有很多種映射,library的映射,函數(shù)的映射還有函數(shù)參數(shù)和返回值的映射,libary和函數(shù)的映射比較簡單,我們?cè)谥暗奈恼轮幸呀?jīng)講解過了,對(duì)于類型映射來說,因?yàn)镴AVA中的類型種類比較多,所以這里我們將JNA的類型映射提取出來單獨(dú)講解。
類型映射的本質(zhì)
我們之前提到在JNA中有兩種方法來映射JAVA中的方法和native libary中的方法,一種方法叫做interface mapping,一種方式叫做direct mapping。
但是我們有沒有考慮過這兩種映射的本質(zhì)是什么呢?
比如native有一個(gè)方法,我們是如何將JAVA代碼中的方法參數(shù)傳遞給native方法,并且將native方法的返回值轉(zhuǎn)換成JAVA中函數(shù)的返回類型呢?
答案就是序列化。
因?yàn)楸举|(zhì)上一切的交互都是二進(jìn)制的交互。JAVA類型和native類型進(jìn)行轉(zhuǎn)換,最簡單的情況就是JAVA類型和native類型底層的數(shù)據(jù)長度保持一致,這樣在進(jìn)行數(shù)據(jù)轉(zhuǎn)換的時(shí)候就會(huì)更加簡單。
我們看下JAVA類型和native類型的映射和長度關(guān)系:
C Type | Native類型的含義 | Java Type |
---|---|---|
char | 8-bit整型 | byte |
wchar_t | 和平臺(tái)相關(guān) | char |
short | 16-bit整型 | short |
int | 32-bit整型 | int |
int | boolean flag | boolean |
enum | 枚舉類型 | int (usually) |
long long, __int64 | 64-bit整型 | long |
float | 32-bit浮點(diǎn)數(shù) | float |
double | 64-bit浮點(diǎn)數(shù) | double |
pointer (e.g. void*) | 平臺(tái)相關(guān) | Buffer Pointer |
pointer (e.g. void*), array | 平臺(tái)相關(guān) | [] (原始類型數(shù)組) |
上面的JAVA類型都是JDK自帶的類型(Pointer除外)。
除了JAVA自帶的類型映射,JNA內(nèi)部也定義了一些數(shù)據(jù)類型,可以跟native的類型進(jìn)行映射:
C Type | Native類型的含義 | Java Type |
---|---|---|
long | 和平臺(tái)相關(guān)(32- or 64-bit integer) | NativeLong |
const char* | 字符串 (native encoding or jna.encoding) | String |
const wchar_t* | 字符串 (unicode) | WString |
char** | 字符串?dāng)?shù)組 | String[] |
wchar_t** | 字符串?dāng)?shù)組(unicode) | WString[] |
void** | pointers數(shù)組 | Pointer[] |
struct* struct | 結(jié)構(gòu)體指針和結(jié)構(gòu)體 | Structure |
union | 結(jié)構(gòu)體 | Union |
struct[] | 結(jié)構(gòu)體數(shù)組 | Structure[] |
void (*FP)() | 函數(shù)指針 (Java or native) | Callback |
pointer ( *) | 指針 | PointerType |
other | 整數(shù)類型 | IntegerType |
other | 自定義映射類型 | NativeMapped |
TypeMapper
除了定義好的映射關(guān)系之外,大家也可以使用TypeMapper來對(duì)參數(shù)類型進(jìn)行自定義轉(zhuǎn)換,先來看下TypeMapper的定義:
public interface TypeMapper { FromNativeConverter getFromNativeConverter(Class<?> javaType); ToNativeConverter getToNativeConverter(Class<?> javaType); }
TypeMapper是一個(gè)interface,它定義了兩個(gè)converter方法,分別是getFromNativeConverter和getToNativeConverter。
如果要使用TypeMapper則需要實(shí)現(xiàn)它而這兩個(gè)方法即可。我們看一下官方的W32APITypeMapper是怎么實(shí)現(xiàn)的:
TypeConverter stringConverter = new TypeConverter() { @Override public Object toNative(Object value, ToNativeContext context) { if (value == null) return null; if (value instanceof String[]) { return new StringArray((String[])value, true); } return new WString(value.toString()); } @Override public Object fromNative(Object value, FromNativeContext context) { if (value == null) return null; return value.toString(); } @Override public Class<?> nativeType() { return WString.class; } }; addTypeConverter(String.class, stringConverter); addToNativeConverter(String[].class, stringConverter);
首先定義一個(gè)TypeConverter,在TypeConverter中實(shí)現(xiàn)了toNative,fromNative和nativeType三個(gè)方法。在這個(gè)例子中,native type是WString,而JAVA type是String。而這個(gè)TypeConverter就是最終要使用的FromNativeConverter和ToNativeConverter。
有了typeMapper,應(yīng)該怎么使用呢?最簡單的方法就是將其添加到Native.load的第三個(gè)參數(shù)中,如下所示:
TestLibrary lib = Native.load("testlib", TestLibrary.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, mapper));
NativeMapped
TypeMapper需要在調(diào)用Native.load方法的時(shí)候傳入,從而提供JAVA類型和native類型的轉(zhuǎn)換關(guān)系。TypeMapper可以看做是類型轉(zhuǎn)換關(guān)系的外部維護(hù)者。
可能很多朋友已經(jīng)想到了,既然能在JAVA類型外部維護(hù)轉(zhuǎn)換關(guān)系,那么可不可以在JAVA類型本身對(duì)這個(gè)轉(zhuǎn)換關(guān)系進(jìn)行維護(hù)呢?答案是肯定的,我們只需要在要實(shí)現(xiàn)轉(zhuǎn)換類型關(guān)系的JAVA類型實(shí)現(xiàn)NativeMapped接口即可。
先來看下NativeMapped接口的定義:
public interface NativeMapped { Object fromNative(Object nativeValue, FromNativeContext context); Object toNative(); Class<?> nativeType(); }
可以看到NativeMapped中定義要實(shí)現(xiàn)的方法基本上和FromNativeConverter、ToNativeConverter中定義的方法一致。
下面舉一個(gè)具體的例子來說明一下NativeMapped到底應(yīng)該怎么使用。首先我們定義一個(gè)enum類實(shí)現(xiàn)NativeMapped接口:
public enum TestEnum implements NativeMapped { VALUE1, VALUE2; @Override public Object fromNative(Object nativeValue, FromNativeContext context) { return values()[(Integer) nativeValue]; } @Override public Object toNative() { return ordinal(); } @Override public Class<?> nativeType() { return Integer.class; } }
這個(gè)類實(shí)現(xiàn)了從Integer到TestEnum枚舉的轉(zhuǎn)換。
要想使用該TestEnum類的話,需要定義一個(gè)interface:
public static interface EnumerationTestLibrary extends Library { TestEnum returnInt32Argument(TestEnum arg); }
具體調(diào)用邏輯如下:
EnumerationTestLibrary lib = Native.load("testlib", EnumerationTestLibrary.class); assertEquals("Enumeration improperly converted", TestEnum.VALUE1, lib.returnInt32Argument(TestEnum.VALUE1)); assertEquals("Enumeration improperly converted", TestEnum.VALUE2, lib.returnInt32Argument(TestEnum.VALUE2));
可以看到,因?yàn)镹ativeMapped中已經(jīng)包含了類型轉(zhuǎn)換的信息,所以不需要再指定TypeMapper了。
注意,這里用到了testlib,這個(gè)testlib是從JNA的native模塊中編譯出來的,如果你是MAC環(huán)境的話可以拷貝JNA代碼,運(yùn)行ant native即可得到,編譯完成之后,將這個(gè)libtestlib.dylib拷貝到你項(xiàng)目中的resources目錄下面darwin-aarch64或者darwin-x86即可。
有不會(huì)的同學(xué),可以聯(lián)系我。
總結(jié)
本文講解了JNA中的類型映射規(guī)則和自定義類型映射的方法。
本文的代碼:https://github.com/ddean2009/learn-java-base-9-to-20
到此這篇關(guān)于java高級(jí)用法之JNA中使用類型映射的文章就介紹到這了,更多相關(guān)JNA 類型映射內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用Cipher類實(shí)現(xiàn)加密的過程詳解
這篇文章主要介紹了Java使用Cipher類實(shí)現(xiàn)加密的過程詳解,Cipher類提供了加密和解密的功能,創(chuàng)建密匙主要使用SecretKeySpec、KeyGenerator和KeyPairGenerator三個(gè)類來創(chuàng)建密匙。感興趣可以了解一下2020-07-07關(guān)于Java中的dozer對(duì)象轉(zhuǎn)換問題
Dozer是Java?Bean到Java?Bean映射器,它以遞歸方式將數(shù)據(jù)從一個(gè)對(duì)象復(fù)制到另一個(gè)對(duì)象,這篇文章主要介紹了Java中的dozer對(duì)象轉(zhuǎn)換的操作方法,需要的朋友可以參考下2022-08-08Java利用IO流實(shí)現(xiàn)簡易的記事本功能
本文將利用Java中IO流編寫一個(gè)模擬日記本的程序,通過在控制臺(tái)輸入指令,實(shí)現(xiàn)在本地新建文件,打開日記本和修改日記本等功能,感興趣的可以了解一下2022-05-05