JNI 和 JNA 使用

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)方法

  1. 獲取這個(gè)類對象的引用 GetObjectClass()
  2. 獲取方法ID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig)
    需要提供方法名和對應(yīng)的簽名
  3. 基于方法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)注冊本地方法的弊端

  1. 需要編譯所有聲明了native方法的Java類,每個(gè)所生成的class文件都得用javah生成一個(gè)頭文件
  2. javah生成的JNI層函數(shù)名特別長,書寫起來很不方便。
  3. 初次調(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ù)類型可以直接映射


Default Type Mappings

數(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è)繼承Structurepublic 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代碼


Reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容