Java Native Interface (JNI)是一個(gè)本地編程接口,可以讓Java代碼使用以其他語言(C/C++) 編寫的代碼和代碼庫。
編寫Java代碼
package myjni;
public class HelloJNI {
static {
System.loadLibrary("hello"); // Load native library at runtime
// hello.dll (Windows) or libhello.so (Unixes)
}
// Declare a native method sayHello() that receives nothing and returns void
private native void sayHello();
// Test Driver
public static void main(String[] args) {
new HelloJNI().sayHello(); // invoke the native method sayHello()
}
}
- 靜態(tài)初始化塊加載本地庫,這個(gè)庫應(yīng)該被包含在Java庫路徑中(java.library.path 變量),否則會(huì)產(chǎn)生
UnsatisfiedLinkError
通過System.getProperty("java.library.path")
可以查詢對應(yīng)的值,在執(zhí)行程序的時(shí)候可以通過VM參數(shù)-Djava.library.path=path_to_lib
顯示指定路徑 - native 關(guān)鍵字聲明方法,表明使用另外一種語言實(shí)現(xiàn)的
在實(shí)際使用中,.so文件會(huì)被放入到
resource
目錄下,通過Class.getResource
或者ClassLoader.getResource
獲取資源,存儲(chǔ)到臨時(shí)文件中,再加載該臨時(shí)文件,而不用通過Djava.library.path
顯示指定路徑
編譯java代碼
成字節(jié)碼 "HelloJNI.java" -> "HelloJNI.class"
> javac myjni\HelloJNI.java
or
> javac -d. myjni\HelloJNI.java //-d 指定放置生成類文件的位置,必要時(shí)創(chuàng)建包名對應(yīng)的目錄
創(chuàng)建 C/C++ 頭文件
創(chuàng)建一個(gè)定義native函數(shù)簽名的C/ C++頭文件
通過使用JDK附帶javah
工具創(chuàng)建一個(gè)頭文件,它可以生成包含class文件中native方法函數(shù)聲明的頭文件
// 如果使用的是IDEA 先cd project/build/class/main
javah -d <dir> myjni.HelloJNICpp // 生成 HelloJNICpp.h 文件
Java10中該工具不再提供,改為使用
javac -h <output_dir> myjni\HelloJNI.java
生成樣式如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_myjni_HelloJNI_sayHello(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
頭文件聲明了一個(gè)C函數(shù)
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject);
C函數(shù)的命名慣例是
Java_{package_and_classname}_{function_name}(JNI arguments)
,包名稱中的點(diǎn)用下劃線替代,函數(shù)默認(rèn)先包含兩個(gè)參數(shù):
-
JNIEnv*
: JNI environment 的引用(jni.h中定義),相當(dāng)于一個(gè)函數(shù)表指針,用來訪問所有的JNI函數(shù) -
jobject
: Java 該對象引用,相當(dāng)于“this”
宏定義JNIEXPORT 和 JNICALL 是對編譯器說明信息,用來做出口函數(shù)
extern "C" 只會(huì)被C++編譯器識(shí)別,表明使用C的函數(shù)命名協(xié)議
C/C++代碼
C代碼如下:
#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"
JNIEXPORT void JNICALL Java_myjni_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello World!\n");
return;
}
Ubuntu下的編譯C代碼,如果是C++需要使用g++編譯
gcc -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libhello.so HelloJNI.c
- -fPIC則表明使用地址無關(guān)代碼,PIC:Position Independent Code
- -I 指定頭文件,例如
jni.h
運(yùn)行Java程序
顯示指定.so文件
> java -Djava.library.path=. myjni.HelloJNI
JNI基礎(chǔ)
- Java基本類型:jint, jbyte, jshort, jlong, jfloat, jdouble, jchar, jboolean 分別對應(yīng)
int, byte, short, long, float, double, char, boolean - Java引用類型:jobject 應(yīng)用對應(yīng)于
java.lang.Object
,它同樣定義了眾多子類型
typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
C/C++方法收到的是JNI類型,返回值也是JNI類型(例如jstring
, jintArray
),但是程序內(nèi)部使用的是C/C++類型,所以中間需要JNI類型與C/C++類型的相互轉(zhuǎn)換
C/C++程序步驟:
- 接收J(rèn)NI類型的參數(shù)(由Java程序傳遞來)
- 對于引用類型(
jObject
),將參數(shù)轉(zhuǎn)換或復(fù)制為本地類型,例如將jstring轉(zhuǎn)換為C字符串,將jintArray轉(zhuǎn)換為C的int []等;對于原始JNI類型,如jint和jdouble不需要轉(zhuǎn)換,可以直接操作 - 以本地類型執(zhí)行操作
- 創(chuàng)建JNI類型的返回對象,并將結(jié)果復(fù)制到返回對象中
JNI編程中關(guān)鍵點(diǎn)是JNI引用類型(如jstring,jobject,jintArray,jobjectArray)和本機(jī)類型(C-string,int [])之間的轉(zhuǎn)換。 JNI接口提供了許多函數(shù)來進(jìn)行這樣的轉(zhuǎn)換
JNI是一個(gè)C接口,它不是面向?qū)ο蟮模詻]有真正傳遞對象
JNIEnv函數(shù)調(diào)用方法
通過JNIEnv可以調(diào)用眾多函數(shù),內(nèi)部實(shí)際上都是JNINativeInterface_
結(jié)構(gòu)體內(nèi)部的函數(shù)指針
struct JNINativeInterface_;
struct JNIEnv_;
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif
類型 | 語法 |
---|---|
C | (*env)->FunctionName(env, parameter) |
C++ | env->FunctionName(parameter) //使用了內(nèi)聯(lián)函數(shù) |
下面的例子中多使用C語言的調(diào)用方式
傳遞原始類型
可以直接使用強(qiáng)制類型轉(zhuǎn)換()
typedef unsigned char jboolean; /* unsigned 8 bits */
typedef signed char jbyte; /* signed 8 bits */
typedef unsigned short jchar; /* unsigned 16 bits */
typedef short jshort; /* signed 16 bits */
typedef int jint; /* signed 32 bits */
typedef long long jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
傳遞字符串
Java的字符串是16位Unicode字符序列,C的字符串是char型數(shù)組,以空字符結(jié)尾
JNIEnv提供了轉(zhuǎn)換的函數(shù)
UTF-8(1到3個(gè)字節(jié)) <--> Unicode(2個(gè)字節(jié))
//If isCopy is not NULL, then *isCopy is set to JNI_TRUE if a copy is made; or it is set to JNI_FALSE if no copy is made.
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
//將jstring轉(zhuǎn)換成為Unicode格式的char*
const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
//告訴VM native 代碼不在訪問utf
void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);
//釋放指向Unicode格式的char*的指針
void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);
//創(chuàng)建一個(gè)java.lang.String 對象
jstring NewStringUTF(JNIEnv *env, const char *bytes);
//創(chuàng)建一個(gè)Unicode格式的String對象,從Unicode字符
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize length);
//獲取UTF-8形式的串長度
jsize GetStringUTFLength(JNIEnv *env, jstring string);
//獲取Unicode格式的的長度
jsize GetStringLength(JNIEnv *env, jstring string);
//把一段區(qū)域中的String,轉(zhuǎn)為UTF-8,放入buf
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize length, char *buf);
//復(fù)制一段區(qū)域中的String,放入buf
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize length, jchar *buf);
GetStringChars
: jstring -> char* 如果內(nèi)存分配失敗,返回NULL,第三個(gè)參數(shù)如果不為NULL,當(dāng)返回字符串是原始string的復(fù)制時(shí),被置為JNI_TRUE;
當(dāng)直接指向初始的String類型時(shí),對應(yīng)為JNI_FALSE ,jni在運(yùn)行時(shí)會(huì)盡可能執(zhí)行這種情況,這時(shí)本地程序如果修改字符數(shù)組的內(nèi)容,對應(yīng)Java程序的字符串會(huì)發(fā)生改變
傳遞原始類型的數(shù)組
// ArrayType: jintArray, jbyteArray, jshortArray, jlongArray, jfloatArray, jdoubleArray, jcharArray, jbooleanArray
// PrimitiveType: int, byte, short, long, float, double, char, boolean
// NativeType: jint, jbyte, jshort, jlong, jfloat, jdouble, jchar, jboolean
NativeType * Get<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, jboolean *isCopy);
void Release<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode);
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize length, NativeType *buffer);
void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize length, const NativeType *buffer);
ArrayType New<PrimitiveType>Array(JNIEnv *env, jsize length);
void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);
jsize GetArrayLength(JNIEnv *env, jarray array);//獲取數(shù)組的長度
訪問對象的變量和方法
訪問實(shí)例對象的變量和類的靜態(tài)變量
jclass GetObjectClass(JNIEnv *env, jobject obj);
// Returns the class of an object.
jfieldID GetFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig);
// Returns the field ID for an instance variable of a class.
jfieldID GetStaticFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig);
// Returns the field ID for a static variable of a class.
// 通過jfieldID讀寫字段值
// <type>包括8種原始類型和object
NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value);
NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID);
void SetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value);
GetFieldID中sig表示簽名,編碼形式如下
類型 | sig |
---|---|
boolen | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
class | L<fully-qualified-name>;//引用類型 以 L 開頭 ; 結(jié)尾 |
方法簽名主要用在重載方法的說明,簽名的形式是“(parameters)return-type”
javap -s -p 類名稱
查看簽名
javap 是java類文件的反編譯器
-s :輸出內(nèi)部類型簽名
-p:顯示私有成員,默認(rèn)只打印public的簽名信息
例子:設(shè)定一個(gè)long變量
static jlong setField_long(JNIEnv *env, jobject java_obj, const char *field_name, jlong val) {
jclass clazz = env->GetObjectClass(java_obj);
jfieldID field = env->GetFieldID(clazz, field_name, "J");
if (field)
env->SetLongField(java_obj, field, val);
else {
LOGE("__setField_long:field '%s' not found", field_name);
}
return val;
}
調(diào)用實(shí)例對象的方法和靜態(tài)方法
- 獲取這個(gè)類對象的引用
GetObjectClass()
- 獲取方法ID
GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig)
需要提供方法名和對應(yīng)的簽名 - 基于方法ID,
Call<Primitive-type>Method()
,CallVoidMethod()
,CallObjectMethod()
,對應(yīng)<Primitive-type>
指的是返回類型,如果有參數(shù),就在后邊加上參數(shù)
JNI中對應(yīng)的函數(shù):
jmethodID GetMethodID(JNIEnv *env, jclass cls, const char *name, const char *sig);
// Returns the method ID for an instance method of a class or interface.
NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...);
NativeType Call<type>MethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
NativeType Call<type>MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
// Invoke an instance method of the object.
// The <type> includes each of the eight primitive and Object.
jmethodID GetStaticMethodID(JNIEnv *env, jclass cls, const char *name, const char *sig);
// Returns the method ID for an instance method of a class or interface.
NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
// Invoke an instance method of the object.
// The <type> includes each of the eight primitive and Object.
//調(diào)用超類的方法
NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, ...);
NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, const jvalue *args);
NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, va_list args);
創(chuàng)建對象和對象數(shù)組
在native代碼中構(gòu)建jobject,jobjectArray
創(chuàng)建對象
獲得構(gòu)造方法的ID,傳遞函數(shù)名為“<init>”,“V”作為返回類型
jclass FindClass(JNIEnv *env, const char *name);
jobject NewObject(JNIEnv *env, jclass cls, jmethodID methodID, ...);
jobject NewObjectA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args);
jobject NewObjectV(JNIEnv *env, jclass cls, jmethodID methodID, va_list args);
// Constructs a new Java object. The method ID indicates which constructor method to invoke
jobject AllocObject(JNIEnv *env, jclass cls);
// Allocates a new Java object without invoking any of the constructors for the object.
對象數(shù)組
- FindClass(env, "java/lang/Double"); 找到對應(yīng)的類
- NewObjectArray(env, 2, classDouble, NULL); 創(chuàng)建對應(yīng)的數(shù)組
- GetMethodID(env, classDouble, "<init>", "(D)V"); 找到類的構(gòu)造方法
- NewObject(env, classDouble, midDoubleInit, average); 創(chuàng)建該類
- SetObjectArrayElement(env, outJNIArray, 0, objSum); 存到數(shù)組對應(yīng)的位置上
jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
// Constructs a new array holding objects in class elementClass.
// All elements are initially set to initialElement.
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
// Returns an element of an Object array.
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
// Sets an element of an Object array.
本地和全局引用
JNI將native代碼中的對象引用分為兩類
- 本地引用
local reference
:native方法中創(chuàng)建的都是本地引用,方法結(jié)束后就回收,可以通過DeleteLocalRef()
顯式的使本地引用無效,使得JVM盡快進(jìn)行GC,同時(shí)傳遞到native方法的參數(shù)對象,jni方法返回的Java對象都是本地引用 - 全局引用
global reference
:通過jobject NewGlobalRef() (JNIEnv *env, jobject lobj);
獲取,然后通過void DeleteGlobalRef(JNIEnv *env, jobject gref);
顯示的釋放
所以對全局變量賦值時(shí),先要將本地引用轉(zhuǎn)為全局引用,賦值后再釋放本地引用
實(shí)際上本地引用并不是native方法里的局部變量,局部變量存放在堆棧中,而Local Reference存放在Local Reference Table中,而該Table的容量是有限的,雖然在native Method結(jié)束時(shí),JVM會(huì)自動(dòng)釋放Local Reference,但在使用中,要及時(shí)使用DeleteLocalRef()
刪除不必要的Local Reference,避免Local Reference Table溢出
動(dòng)態(tài)加載
VM虛擬機(jī)會(huì)在native庫加載的時(shí)候調(diào)用JNI_OnLoad
,例如通過System.loadLibrary
加載的時(shí)候
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);
靜態(tài)注冊本地方法的弊端
- 需要編譯所有聲明了native方法的Java類,每個(gè)所生成的class文件都得用javah生成一個(gè)頭文件
- javah生成的JNI層函數(shù)名特別長,書寫起來很不方便。
- 初次調(diào)用native函數(shù)時(shí)要根據(jù)函數(shù)名字搜索對應(yīng)用JNI層函數(shù)來建立關(guān)聯(lián)關(guān)系,這樣會(huì)影響運(yùn)行效率。
動(dòng)態(tài)注冊核心是JNINativeMethod
,把Java中的方法和本地的方法關(guān)聯(lián)起來
typedef struct {
char *name; //Java中原生方法的名稱
char *signature;//方法簽名,是參數(shù)類型和返回值類型的組合
void *fnPtr;//函數(shù)指針,
} JNINativeMethod;
看了其他的教程,發(fā)現(xiàn)都是固定的套路,C++代碼如下:
static JNINativeMethod gMethods[] = {
{"name", "signature", fnptr},
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = NULL;
jint result = JNI_FALSE;
//獲取env指針
if (jvm->GetEnv(reinterpret_cast<void **> (&env), JNI_VERSION_1_6.) != JNI_OK) {
return result;
}
if (env == NULL) {
return result;
}
//獲取類引用,寫類的全路徑(包名+類名
jclass clazz = env->FindClass("***/***/JNIDynamicUtils");
if (clazz == NULL) {
return result;
}
//注冊方法
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
return result;
}
//成功
result = JNI_VERSION_1_6;
return result;
}
在JNI_OnLoad中,可以保存JavaVM
的指針,這是跨線程的,持久有效的變量
多線程
JNIEnv只在當(dāng)前線程有效,如果C/C++中創(chuàng)建的新線程需要訪問Java VM,先調(diào)用AttachCurrentThread
方法把自己附著到VM并且獲得env
JavaVM *vm;
JNIEnv *env;
vm->AttachCurrentThread(&env, NULL);
vm->DetachCurrentThread();//卸載
多Java線程加載同一個(gè)lib,或者單一線程加載多次,實(shí)際上都不會(huì)進(jìn)行處理,因?yàn)?code>ClassLoader內(nèi)部靜態(tài)字段會(huì)記錄進(jìn)程加載的所有共享文件,首先會(huì)檢查該ClassLoad是否加載過,不會(huì)進(jìn)行重復(fù)加載,如果其他ClassLoad加載過,也不會(huì)進(jìn)行加載,多個(gè)線程調(diào)用同一個(gè)so文件的不同函數(shù),共享一套全局變量,因?yàn)楣蚕韼煸谶M(jìn)程中只有一套數(shù)據(jù)
ClassLoad中關(guān)于加載native library的核心字段:
// 所有加載的native library名稱
private static Vector<String> loadedLibraryNames = new Vector<>();
// Native libraries belonging to system classes.
private static Vector<NativeLibrary> systemNativeLibraries
= new Vector<>();
//當(dāng)前class loader對應(yīng)的加載的所有Native libraries
private Vector<NativeLibrary> nativeLibraries = new Vector<>();
// The paths searched for libraries
private static String usr_paths[];
private static String sys_paths[];
底層具體通過dlopen
庫函數(shù)加載:
jdk/src/share/native/java/lang/ClassLoader.c#Java_java_lang_ClassLoader_00024NativeLibrary_load
src/share/vm/prims/jvm.cpp#JVM_LoadLibrary
/src/os/linux/vm/os_linux.cpp#dll_load#os::Linux::dlopen_helper#dlopen(filename, RTLD_LAZY)
JNA
JNA(Java Native Access)是一個(gè)開源的Java框架,是Sun公司推出的一種調(diào)用本地方法的技術(shù),建立在JNI基上的一個(gè)框架。JNA簡化了調(diào)用本地方法的過程,可以直接調(diào)用本地共享庫中的函數(shù),不需要寫Java代碼之外的程序
需要添加依賴的jar包jna.jar
到CLASSPATH,也可以再添加jna-platform.jar
包,內(nèi)部包含一些平臺(tái)常用的庫映射
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
</dependency>
使用方法:
package com.sun.jna.examples;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
/** Simple example of JNA interface mapping and usage. */
public class HelloWorld {
// This is the standard, stable way of mapping, which supports extensive
// customization and mapping of Java to native types.
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.load((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
for (int i=0;i < args.length;i++) {
CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
}
}
}
- 一個(gè)接口類映射一個(gè)要加載的庫,該類需要擴(kuò)展自
Library
,接口內(nèi)定義需要使用的本地庫方法 - 接口內(nèi)部需要一個(gè)公共靜態(tài)常量:
INSTANCE
,通過這個(gè)常量,就可以獲得這個(gè)接口的實(shí)例,從而使用接口的方法,JNA內(nèi)部通過代理模式,先對數(shù)據(jù)類型進(jìn)行轉(zhuǎn)換,調(diào)用外部dll/so的函數(shù) - 可以將本地庫路徑設(shè)定到
jna.library.path
核心是不同平臺(tái)數(shù)據(jù)類型的轉(zhuǎn)變:
同JNI一樣,Java基本數(shù)據(jù)類型可以直接映射
數(shù)組類型:
// Original C declarations
void fill_buffer(int *buf, int len);
void fill_buffer(int buf[], int len); // same thing with array syntax
// Equivalent JNA mapping
void fill_buffer(int[] buf, int len);
結(jié)構(gòu)體對應(yīng):定義一個(gè)繼承Structure
的 public static
類,用來作為參數(shù)或返回值類型,類中的公共字段的順序,必須與C 語言中的結(jié)構(gòu)的順序一致,同時(shí)定義getFieldOrder()
方法,返回有序的字段名稱
Java 調(diào)用動(dòng)態(tài)鏈接庫中的C 函數(shù),實(shí)際上就是一段內(nèi)存作為函數(shù)的參數(shù)傳遞給C函數(shù)。動(dòng)態(tài)鏈接庫以為這個(gè)參數(shù)就是C 語言傳過來的參數(shù)。同時(shí),C 語言的結(jié)構(gòu)體是一個(gè)嚴(yán)格的規(guī)范,它定義了內(nèi)存的次序。因此,JNA 中模擬的結(jié)構(gòu)體的變量順序絕對不能錯(cuò)
JNA是不能完全替代JNI的,因?yàn)镹A只能實(shí)現(xiàn)Java訪問C函數(shù),使用JNI技術(shù),不僅可以實(shí)現(xiàn)Java訪問C函數(shù),也可以實(shí)現(xiàn)C語言調(diào)用Java代碼