日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JNI 学习笔记

發布時間:2024/4/15 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JNI 学习笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://baike.baidu.com/item/JNI/9412164?fr=aladdin

https://www.jianshu.com/p/f6e3dd8edb13

? JellyJoe_943? 2016.09.27 18:58*?字數 2769?閱讀 1673評論 1

JNI 學習筆記

1.概述

Java Native Interface(JNI) 是JDK提供的一個native編程接口。JNI 允許Java程序調用其他語言編寫的程序或者代碼庫, 比如C/C++。Java 在內存管理和性能上有一定的局限,通過JNI我們就可以利用Native程序來克服這些限制。

2.一個簡單的demo

通過一個簡單的Demo我們來看一下Native 與 Java 的互相調用。

Java :HelloJNI.java

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 } }

執行?javac HelloJNI.java?編譯java 文件

C:

創建頭文件:HelloJNI.h, 可以通過javah 工具自動生成:

執行:>javah HelloJNI?這里HelloJNI指的是編譯后的.class文件

/* 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_HelloJNI_sayHello(JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif

C 程序實現:

#include <jni.h> #include <stdio.h> #include "HelloJNI.h" // Implementation of native method sayHello() of HelloJNI class JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) { printf("Hello World!\n"); return; }

簡單解析:

Java 調用Native , 需要:

  • 通過System.loadLibrary("hello")?方法加載native 模塊"Hello"(該模塊包含了syHello()方法)。
  • 聲明native 方法 :?private native void sayHello();
  • 在main方法中調用

C/C++ 需要實現:

  • 定義頭文件, 可以通過javah自動生成:?javah HelloJNI?, 我們可以看到對應java中定義的sayHello()方法, 在頭文件中生成了JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject);方法。其格式可以概括為JNIEXPORT <返回類型> JNICALL Java_<包名>_<類名>_<方法名>(JNIEnv *, jobject,<參數>);?包名中的(.)用下劃線(_)代替。 參數中JNIEnv *?是JNI環境, 提供了許多方法, 后面我們會提到。 jobject是調用的java對象。
  • 在C/C++文件中實現具體方法

3.JNI 基礎

3.1 JNI類型

JNI 定義了以下類型來對應Java中的類型:

a. 基本類型: jni -> java

  • jint --> int
  • jbyte --> byte
  • jshort --> short
  • jlong --> long
  • jfloat --> float
  • jdouble --> double
  • jchar --> char
  • jboolean --> boolean

b. 引用類型:

  • jclass --> java.lang.Class
  • jstring --> java.lang.String
  • jthrowable --> java.lang.Throwable
  • jarray --> Java數組

3.2參數傳遞

3.2.1. Java 基本類型傳遞

對于Java 的基本類型, 我們可以直接傳遞參數, 在原生系統中已經定義了jxxx中的基本類型, 比如?jint, jbyte, jshort, jlong, jfloat, jdouble, jchar, jboolean?對應 Java中的?int, byte, short, long, float, double, char, boolean

在jni.h?和?win/jni_mh定義了以上8種JNI環境下的基本類型, 除此之外還額外定義了jsize

// In "win\jni_mh.h" - machine header which is machine dependent typedef long jint; typedef __int64 jlong; typedef signed char jbyte; // In "jni.h" typedef unsigned char jboolean; typedef unsigned short jchar; typedef short jshort; typedef float jfloat; typedef double jdouble; typedef jint jsize;

需要注意的是, jint 對應的是long類型(至少是32位的),在C語言的基本類型中, int 可能是只有16位, 這樣就無法對應Java中的int類型了。 所以為保險起見, JNI編程中建議在C/C++程序中使用jint代替int.

3.2.2. String 類型傳遞

JNI 中定義了 jstring 類型代替Java中的String 類型,String 類型的傳遞相比基本類型要復雜, 因為在Java中String 是個對象, 而在c中, string 是個 char類型數組。所以在傳遞String類型的時候, 在String(被jstring替換) 類型和 (char*)類型之間做轉化。

JNI環境提供了一些轉換方法, 可以通過JNIEnv*?參數調用這些轉化方法:

  • 從jstring 中獲取c-string, 即char* :?const char* GetStringUTFChars(JNIEnv*, jstring, jboolean*)
  • 從C-string(char*) 中獲取jstring :?jstring NewStringUTF(JNIEnv*, char*)

JNI Native String Functions

JNI 支持unicode(16位)編碼和utf-8(1-3個字節)編碼的string 類型轉換, utf-8 編碼的string 和 C-string(char*)的行為類似, 在 C/C++程序中使用。

UTF-8 strings的轉換方法

// UTF-8 String (encoded to 1-3 byte, backward compatible with 7-bit ASCII) // Can be mapped to null-terminated char-array C-string const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy); // Returns a pointer to an array of bytes representing the string in modified UTF-8 encoding. void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf); // Informs the VM that the native code no longer needs access to utf. jstring NewStringUTF(JNIEnv *env, const char *bytes); // Constructs a new java.lang.String object from an array of characters in modified UTF-8 encoding. jsize GetStringUTFLength(JNIEnv *env, jstring string); // Returns the length in bytes of the modified UTF-8 representation of a string. void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize length, char *buf); // Translates len number of Unicode characters beginning at offset start into modified UTF-8 encoding // and place the result in the given buffer buf.
  • GetStringUTFChars()?方法可以用于通過 jstring 創建一個 C-string (char*), 如果內存無法分配, 則返回NULL。

其中第三個參數isCopy?如果設為JNI_TRUE , 則返回結果是原始Java String 的拷貝, 如果設為JNI_FALSE, 則直接返回 Java String 的地址, 然而在這種情況下, 無法對string內容進行修改。JNI在運行時會試圖返回指針, 如果可以的話,否則會返回一個拷貝。 通常情況下, 我們并不關心底層string 的內容呢, 所以通常都設為 NULL。

當你不需要使用該方法返回的結果時, 可以調用?ReleaseStringUTFChars()?來釋放內存以及引用。

  • NewStringUTF()?方法則是通過c-string 創建一個 JNI String (jstring)

想要了解更多可以查看:?http://docs.oracle.com/javase/7/docs/technotes/guides/jni/index.html.

Unicode String

// Unicode Strings (16-bit character) const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy); // Returns a pointer to the array of Unicode characters void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars); // Informs the VM that the native code no longer needs access to chars. jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize length); // Constructs a new java.lang.String object from an array of Unicode characters. jsize GetStringLength(JNIEnv *env, jstring string); // Returns the length (the count of Unicode characters) of a Java string. void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize length, jchar *buf); // Copies len number of Unicode characters beginning at offset start to the given buffer buf

對與Unicode 編碼的類型, 使用jchar* 代替 char* 存儲字符。

3.2.3 基本類型的數組傳遞

在Java中, 數組是一個引用類型, 像一個類。 Java 數組有9種, 除了8種基本類型的數組外, 還有一類對象數組, 即java.lang.Object類型的數組。 在JNI 中定義了 8種基本類型的數組對應Java 的8種基本類型數組,jintArray, jbyteArray, jshortArray, jlongArray, jfloatArray, jdoubleArray, jcharArray, jbooleanArray?, 和一種對象數組jobjectArray對應Java中的對象數組。

因此, 你需要在數組傳遞是處理JNI 數組和Native數組之間的轉換, 比如?jintArray <-> jint[], jdoubleArray <-> jdouble[]?等。 JNI 環境已經提供了一些轉換方法, 以jintArray為例:

  • jintArray(JNI) --> (Native)jint[] :?jint* GetIntArrayElements(JNIEnv *env, jintArray a, jboolean *iscopy)
  • jint[] --> jintArray : 調用?jintArray NewIntArray(JNIEnv *env, jsize len)?分配內存, 然后調用?SetIntArrayRegion(JNIEnv *env, jintArray a, jsize start, jsize len, const jint *buf)?將jin[]?拷貝到?jintArray

JNI 中有8組上述方法, 分別對應8中基本類型:

// 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);
  • GET|Release<PrimitiveType>ArrayElements()?用于根據jxxxArray創建 jxxx[]
  • GET|Set<PrimitiveType>ArrayRegion()?可以用于拷貝一個jxxxArray(或者其中一部分)到一個 預分配(pre-allocated)存儲的 jxxx[]
  • New<PrimitiveType>Array()?用于為jxxxArray分配內存, 然后調用?Set<PrimitiveType>ArrayRegion()?方法 將jxxx[] 設值。
  • Get|ReleasePrimitiveArrayCritical()?則是在get 和 release周期之間, 不允許阻塞調用(blocking calls)。

Example:

java:

public class TestJNIPrimitiveArray { static { System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes) } // Declare a native method sumAndAverage() that receives an int[] and // return a double[2] array with [0] as sum and [1] as average private native double[] sumAndAverage(int[] numbers); // Test Driver public static void main(String args[]) { int[] numbers = {22, 33, 33}; double[] results = new TestJNIPrimitiveArray().sumAndAverage(numbers); System.out.println("In Java, the sum is " + results[0]); System.out.println("In Java, the average is " + results[1]); } }

c:

#include <jni.h> #include <stdio.h> #include "TestJNIPrimitiveArray.h" JNIEXPORT jdoubleArray JNICALL Java_TestJNIPrimitiveArray_sumAndAverage (JNIEnv *env, jobject thisObj, jintArray inJNIArray) { // Step 1: Convert the incoming JNI jintarray to C's jint[] jint *inCArray = (*env)->GetIntArrayElements(env, inJNIArray, NULL); if (NULL == inCArray) return NULL; jsize length = (*env)->GetArrayLength(env, inJNIArray); // Step 2: Perform its intended operations jint sum = 0; int i; for (i = 0; i < length; i++) { sum += inCArray[i]; } jdouble average = (jdouble)sum / length; (*env)->ReleaseIntArrayElements(env, inJNIArray, inCArray, 0); // release resources jdouble outCArray[] = {sum, average}; // Step 3: Convert the C's Native jdouble[] to JNI jdoublearray, and return jdoubleArray outJNIArray = (*env)->NewDoubleArray(env, 2); // allocate if (NULL == outJNIArray) return NULL; (*env)->SetDoubleArrayRegion(env, outJNIArray, 0 , 2, outCArray); // copy return outJNIArray; }

Native程序的處理可以概括為3個步驟:

  • Step1: 將傳入的jxxxArray 轉換成 Native 的jxxx[]
  • Step2: 對jxxx[]進行必要處理, 實現期望的功能
  • Step3:將Native結果 轉換成 jxxxArray返回

4. 訪問Java對象中的成員和回調方法

4.1 訪問對象中的成員變量

JNI 環境提供了一些訪問成員變量的方法如下:

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. NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID); void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, NativeType value); // Get/Set the value of an instance variable of an object // <type> includes each of the eight primitive types plus Object.

通過以上方法,我們就可以實現在Native代碼中訪問Java對象的成員變量了, 具體實現可以概括為以下幾個步驟:

  • Step1: 通過GetObjectClass()?方法獲取該對象的類的引用;
  • Step2: 通過GetFieldID()?方法從類引用中(Step1得到該引用)獲取FieldID; 調用該方法需要傳入成員變量的名稱和對應field的描述(descriptor)(或者簽名(signature))。描述的內容具體如下:
    • 對于一個Java類而言, Field 的描述格式為"L<fully-qualified-name>;", 以(/) 代替包名中的(.)。比如String?類型的descrtptor為"Ljava/lang/String;"。(分號不能漏!!!)
    • 對于基本類型,?"I" --> int, "B" --> byte, "S" --> short, "J" --> long, "F" --> float, "D" --> double, "C" --> char, "Z" --> boolean.
    • 對于數組而言, 對象數組以"["作為前綴, 比如"[Ljava/lang/Object;"為一個Object數組的描述,[I?為int的描述。
  • 基于FieldID, 我們可以通過GetObjectField()?或者Get<primitive-type>Field()?方法訪問實例的成員變量。
  • 更新實例中成員變量的值, 可以通過SetObjectField()?或者?Set<primitive-type>Field()?來修改內容, 這里需要傳入參數FieldID.

Example:

java:

public class TestJNIInstanceVariable { static { System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes) } // Instance variables private int number = 88; private String message = "Hello from Java"; // Declare a native method that modifies the instance variables private native void modifyInstanceVariable(); // Test Driver public static void main(String args[]) { TestJNIInstanceVariable test = new TestJNIInstanceVariable(); test.modifyInstanceVariable(); System.out.println("In Java, int is " + test.number); System.out.println("In Java, String is " + test.message); } }

C:

#include <jni.h> #include <stdio.h> #include "TestJNIInstanceVariable.h" JNIEXPORT void JNICALL Java_TestJNIInstanceVariable_modifyInstanceVariable (JNIEnv *env, jobject thisObj) { // Get a reference to this object's class jclass thisClass = (*env)->GetObjectClass(env, thisObj); // int // Get the Field ID of the instance variables "number" jfieldID fidNumber = (*env)->GetFieldID(env, thisClass, "number", "I"); if (NULL == fidNumber) return; // Get the int given the Field ID jint number = (*env)->GetIntField(env, thisObj, fidNumber); printf("In C, the int is %d\n", number); // Change the variable number = 99; (*env)->SetIntField(env, thisObj, fidNumber, number); // Get the Field ID of the instance variables "message" jfieldID fidMessage = (*env)->GetFieldID(env, thisClass, "message", "Ljava/lang/String;"); if (NULL == fidMessage) return; // String // Get the object given the Field ID jstring message = (*env)->GetObjectField(env, thisObj, fidMessage); // Create a C-string with the JNI String const char *cStr = (*env)->GetStringUTFChars(env, message, NULL); if (NULL == cStr) return; printf("In C, the string is %s\n", cStr); (*env)->ReleaseStringUTFChars(env, message, cStr); // Create a new C-string and assign to the JNI string message = (*env)->NewStringUTF(env, "Hello from C"); if (NULL == message) return; // modify the instance variables (*env)->SetObjectField(env, thisObj, fidMessage, message); }

4.2 訪問類的靜態變量

訪問靜態變量與訪問成員變量類似, 只是調用的方法不同, 比如?GetStaticFieldID(), Get|SetStaticObjectField(), Get|SetStatic<Primitive-type>Field()?:

jfieldID GetStaticFieldID(JNIEnv *env, jclass cls, const char *name, const char *sig); // Returns the field ID for a static variable of a class. NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID); void SetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID, NativeType value); // Get/Set the value of a static variable of a class. // <type> includes each of the eight primitive types plus Object.

Example:

Java:

public class TestJNIStaticVariable { static { System.loadLibrary("myjni"); // nyjni.dll (Windows) or libmyjni.so (Unixes) } // Static variables private static double number = 55.66; // Declare a native method that modifies the static variable private native void modifyStaticVariable(); // Test Driver public static void main(String args[]) { TestJNIStaticVariable test = new TestJNIStaticVariable(); test.modifyStaticVariable(); System.out.println("In Java, the double is " + number); } }

C:

#include <jni.h> #include <stdio.h> #include "TestJNIStaticVariable.h" JNIEXPORT void JNICALL Java_TestJNIStaticVariable_modifyStaticVariable (JNIEnv *env, jobject thisObj) { // Get a reference to this object's class jclass cls = (*env)->GetObjectClass(env, thisObj); // Read the int static variable and modify its value jfieldID fidNumber = (*env)->GetStaticFieldID(env, cls, "number", "D"); if (NULL == fidNumber) return; jdouble number = (*env)->GetStaticDoubleField(env, cls, fidNumber); printf("In C, the double is %f\n", number); number = 77.88; (*env)->SetStaticDoubleField(env, cls, fidNumber, number); }

4.3 Native回調Java成員方法和靜態方法

Native 回調 Java實例成員方法

  • Step1: 通過GetObjectClass()?獲取類的引用
  • Step2: 從引用中獲取 MethodID, 通過調用GetMethodID().?這里需要傳入方法名和簽名。 簽名的格式為"(parameters)return-type".,即(參數類型...)返回類型。 你可以使用javap工具列出一個類中方法的簽名,-s?打印簽名,?-p?現實私有方法:
> javap --help > javap -s -p TestJNICallBackMethod.......private void callback(); Signature: ()V private void callback(java.lang.String); Signature: (Ljava/lang/String;)V private double callbackAverage(int, int); Signature: (II)D private static java.lang.String callbackStatic(); Signature: ()Ljava/lang/String; .......
  • Step3: 基于Method ID , 就可以通過調用?Call<Primitive-type>Method() or CallVoidMethod() or CallObjectMethod()方法回調Java實例中的成員方法。

回調靜態的方法步驟與毀掉成員方法類似, 只是方法不同。 JNI 中提供的方法如下:

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.

Example:

Java:

public class TestJNICallBackMethod { static { System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes) } // Declare a native method that calls back the Java methods below private native void nativeMethod(); // To be called back by the native code private void callback() { System.out.println("In Java"); } private void callback(String message) { System.out.println("In Java with " + message); } private double callbackAverage(int n1, int n2) { return ((double)n1 + n2) / 2.0; } // Static method to be called back private static String callbackStatic() { return "From static Java method"; } // Test Driver public static void main(String args[]) { new TestJNICallBackMethod().nativeMethod(); } }

C:

#include <jni.h> #include <stdio.h> #include "TestJNICallBackMethod.h" JNIEXPORT void JNICALL Java_TestJNICallBackMethod_nativeMethod (JNIEnv *env, jobject thisObj) { // Get a class reference for this object jclass thisClass = (*env)->GetObjectClass(env, thisObj); // Get the Method ID for method "callback", which takes no arg and return void jmethodID midCallBack = (*env)->GetMethodID(env, thisClass, "callback", "()V"); if (NULL == midCallBack) return; printf("In C, call back Java's callback()\n"); // Call back the method (which returns void), baed on the Method ID (*env)->CallVoidMethod(env, thisObj, midCallBack); jmethodID midCallBackStr = (*env)->GetMethodID(env, thisClass, "callback", "(Ljava/lang/String;)V"); if (NULL == midCallBackStr) return; printf("In C, call back Java's called(String)\n"); jstring message = (*env)->NewStringUTF(env, "Hello from C"); (*env)->CallVoidMethod(env, thisObj, midCallBackStr, message); jmethodID midCallBackAverage = (*env)->GetMethodID(env, thisClass, "callbackAverage", "(II)D"); if (NULL == midCallBackAverage) return; jdouble average = (*env)->CallDoubleMethod(env, thisObj, midCallBackAverage, 2, 3); printf("In C, the average is %f\n", average); jmethodID midCallBackStatic = (*env)->GetStaticMethodID(env, thisClass, "callbackStatic", "()Ljava/lang/String;"); if (NULL == midCallBackStatic) return; jstring resultJNIStr = (*env)->CallStaticObjectMethod(env, thisClass, midCallBackStatic); const char *resultCStr = (*env)->GetStringUTFChars(env, resultJNIStr, NULL); if (NULL == resultCStr) return; printf("In C, the returned string is %s\n", resultCStr); (*env)->ReleaseStringUTFChars(env, resultJNIStr, resultCStr); }

4.4 回調重寫父類的成員方法

JNI 提供的方法如下:

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);

具體回調實現與普通成員方法一樣, 只是調用JNI提供的不同方法。

5 創建對象和對象數組

通過NewObject?方法和newObjectArray()方法, 我們可以在Native中創建jobject和?jobjectArray, 并將其返回給Java程序。

5.1 在Native程序中回調Java程序的構造函數來創建一個Java對象

回調構造方法和回調成員方法類似。首先獲取構造函數的Method ID. 方法名為"<init>", 返回類型為"V". 然后就可以通過調用NewObject()?方法調用構造函數來構建一個java對象。

JNI 提供的創建對象(jobject)的方法如下:

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.

Example:

Java:

public class TestJNIConstructor { static { System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes) } // Native method that calls back the constructor and return the constructed object. // Return an Integer object with the given int. private native Integer getIntegerObject(int number); public static void main(String args[]) { TestJNIConstructor obj = new TestJNIConstructor(); System.out.println("In Java, the number is :" + obj.getIntegerObject(9999)); } }

C:

#include <jni.h> #include <stdio.h> #include "TestJNIConstructor.h" JNIEXPORT jobject JNICALL Java_TestJNIConstructor_getIntegerObject (JNIEnv *env, jobject thisObj, jint number) { // Get a class reference for java.lang.Integer jclass cls = (*env)->FindClass(env, "java/lang/Integer"); // Get the Method ID of the constructor which takes an int jmethodID midInit = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); if (NULL == midInit) return NULL; // Call back constructor to allocate a new instance, with an int argument jobject newObj = (*env)->NewObject(env, cls, midInit, number); // Try runnning the toString() on this newly create object jmethodID midToString = (*env)->GetMethodID(env, cls, "toString", "()Ljava/lang/String;"); if (NULL == midToString) return NULL; jstring resultStr = (*env)->CallObjectMethod(env, newObj, midToString); const char *resultCStr = (*env)->GetStringUTFChars(env, resultStr, NULL); printf("In C: the number is %s\n", resultCStr); return newObj; }

5.2 對象數組

與基本類型的數組不同, 對象數組不能批量處理, 需要通過Get|SetObjectArrayElement()?方法一一處理。

JNI 提供創建和操作對象數組的方法如下:

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.

Example

Java:

import java.util.ArrayList;public class TestJNIObjectArray { static { System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes) } // Native method that receives an Integer[] and // returns a Double[2] with [0] as sum and [1] as average private native Double[] sumAndAverage(Integer[] numbers); public static void main(String args[]) { Integer[] numbers = {11, 22, 32}; // auto-box Double[] results = new TestJNIObjectArray().sumAndAverage(numbers); System.out.println("In Java, the sum is " + results[0]); // auto-unbox System.out.println("In Java, the average is " + results[1]); } }

C:

#include <jni.h> #include <stdio.h> #include "TestJNIObjectArray.h" JNIEXPORT jobjectArray JNICALL Java_TestJNIObjectArray_sumAndAverage (JNIEnv *env, jobject thisObj, jobjectArray inJNIArray) { // Get a class reference for java.lang.Integer jclass classInteger = (*env)->FindClass(env, "java/lang/Integer"); // Use Integer.intValue() to retrieve the int jmethodID midIntValue = (*env)->GetMethodID(env, classInteger, "intValue", "()I"); if (NULL == midIntValue) return NULL; // Get the value of each Integer object in the array jsize length = (*env)->GetArrayLength(env, inJNIArray); jint sum = 0; int i; for (i = 0; i < length; i++) { jobject objInteger = (*env)->GetObjectArrayElement(env, inJNIArray, i); if (NULL == objInteger) return NULL; jint value = (*env)->CallIntMethod(env, objInteger, midIntValue); sum += value; } double average = (double)sum / length; printf("In C, the sum is %d\n", sum); printf("In C, the average is %f\n", average); // Get a class reference for java.lang.Double jclass classDouble = (*env)->FindClass(env, "java/lang/Double"); // Allocate a jobjectArray of 2 java.lang.Double jobjectArray outJNIArray = (*env)->NewObjectArray(env, 2, classDouble, NULL); // Construct 2 Double objects by calling the constructor jmethodID midDoubleInit = (*env)->GetMethodID(env, classDouble, "<init>", "(D)V"); if (NULL == midDoubleInit) return NULL; jobject objSum = (*env)->NewObject(env, classDouble, midDoubleInit, (double)sum); jobject objAve = (*env)->NewObject(env, classDouble, midDoubleInit, average); // Set to the jobjectArray (*env)->SetObjectArrayElement(env, outJNIArray, 0, objSum); (*env)->SetObjectArrayElement(env, outJNIArray, 1, objAve); return outJNIArray; }

本地引用和全局引用

JNI 將對象引用(針對jobject)根據Native中的使用情況分為兩類: 本地引用(local)和全局引用(global):

  • 本地引用在Native方法中創建, 方法結束時釋放, 僅在方法內有效。當然也可以直接調用DeleteLocalRef()?方法使本地引用失效, 這樣就可以馬上GC了。 所有Java 程序傳遞到Native方法的引用都是本地引用。 所有JNI 方法返回的jobject也是本地引用。
  • 一個全局引用在程序員將它釋放之前會一直存在。JNI 提供的釋放方法為DeleteGlobalRef()。同時, JNI 也提供了通過本地引用創建全局引用的方法NewGlobalRef()。

Example

Java:

public class TestJNIReference { static { System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes) } // A native method that returns a java.lang.Integer with the given int. private native Integer getIntegerObject(int number); // Another native method that also returns a java.lang.Integer with the given int. private native Integer anotherGetIntegerObject(int number); public static void main(String args[]) { TestJNIReference test = new TestJNIReference(); System.out.println(test.getIntegerObject(1)); System.out.println(test.getIntegerObject(2)); System.out.println(test.anotherGetIntegerObject(11)); System.out.println(test.anotherGetIntegerObject(12)); System.out.println(test.getIntegerObject(3)); System.out.println(test.anotherGetIntegerObject(13)); } }

C:

#include <jni.h> #include <stdio.h> #include "TestJNIReference.h" // Global Reference to the Java class "java.lang.Integer" static jclass classInteger; static jmethodID midIntegerInit; jobject getInteger(JNIEnv *env, jobject thisObj, jint number) { // Get a class reference for java.lang.Integer if missing if (NULL == classInteger) { printf("Find java.lang.Integer\n"); classInteger = (*env)->FindClass(env, "java/lang/Integer"); } if (NULL == classInteger) return NULL; // Get the Method ID of the Integer's constructor if missing if (NULL == midIntegerInit) { printf("Get Method ID for java.lang.Integer's constructor\n"); midIntegerInit = (*env)->GetMethodID(env, classInteger, "<init>", "(I)V"); } if (NULL == midIntegerInit) return NULL; // Call back constructor to allocate a new instance, with an int argument jobject newObj = (*env)->NewObject(env, classInteger, midIntegerInit, number); printf("In C, constructed java.lang.Integer with number %d\n", number); return newObj; } JNIEXPORT jobject JNICALL Java_TestJNIReference_getIntegerObject (JNIEnv *env, jobject thisObj, jint number) { return getInteger(env, thisObj, number); } JNIEXPORT jobject JNICALL Java_TestJNIReference_anotherGetIntegerObject (JNIEnv *env, jobject thisObj, jint number) { return getInteger(env, thisObj, number); }

在以上程序中, 我們通過FindClass()方法獲取java.lang.Integer類的引用, 并將它存儲到全局的靜態變量中。然而, 在下一次調用是, 這個引用將不可用(而且不為NULL), 因為FindClass()?返回的是一個局部變量, 當方法結束是就會失效。

為了解決這個問題, 我們需要通過FindClass()返回的引用創建一個全局引用:

// Get a class reference for java.lang.Integer if missingif (NULL == classInteger) {printf("Find java.lang.Integer\n"); // FindClass returns a local reference jclass classIntegerLocal = (*env)->FindClass(env, "java/lang/Integer"); // Create a global reference from the local reference classInteger = (*env)->NewGlobalRef(env, classIntegerLocal); // No longer need the local reference, free it! (*env)->DeleteLocalRef(env, classIntegerLocal); }

參考資料

  • ava Programming Tutorial
    Java Native Interface (JNI)?https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html

轉載于:https://www.cnblogs.com/kelelipeng/p/10368963.html

總結

以上是生活随笔為你收集整理的JNI 学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。