訪問數組(JNI)


JNI在處理基本類型數組和對象數組上面是不同的。對象數組里面是一些指向對象實例或者其它數組的引用。 

基本類型數組:

獲取數組元素指針的對應關系:
  函數            數組類型
  GetBooleanArrayElements   boolean
   GetByteArrayElements    byte
  GetCharArrayElements     char
  GetShortArrayElements    short
  GetIntArrayElements     int
   GetLongArrayElements    long
  GetFloatArrayElements     float
  GetDoubleArrayElements   double
  
釋放數組元素指針的對應關系:
  Function            Array Type
   ReleaseBooleanArrayElements   boolean
   ReleaseByteArrayElements    byte
   ReleaseCharArrayElements    char
   ReleaseShortArrayElements    short
   ReleaseIntArrayElements     int
   ReleaseLongArrayElements    long
   ReleaseFloatArrayElements    float
   ReleaseDoubleArrayElements   double

看一個簡單的例子。下面的程序調用了一個本地方法 sumArray,這個方法對一個 int數組里面的元素進行累加:

class IntArray { 
     private native int sumArray(int[] arr); 
     public static void main(String[] args) { 
         IntArray p = new IntArray(); 
         int arr[] = new int[10]; 
         for (int i = 0; i < 10; i++) { 
             arr[i] = i; 
         } 
         int sum = p.sumArray(arr); 
         System.out.println("sum = " + sum); 
     } 
     static { 
         System.loadLibrary("IntArray"); 
     } 
 } 

數組的引用類型是一般是jarray或者jarray的子類型jintArray。就像jstring不是一個C字符串類型一樣,jarray也不是一個C數組類型。
所以,不要直接訪問 jarray。你必須使用合適的JNI函數來訪問基本數組元素:

使用GetIntArrayRegion 函數來把一個 int數組中的所有元素復制到一個C緩沖區中,然后我們在本地代碼中通過C緩沖區來訪問這些元素。

JNIEXPORT jint JNICALL  Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr) 
{ 
     jint buf[10]; 
     jint i, sum = 0; 
     (*env)->GetIntArrayRegion(env, arr, 0, 10, buf); 
     
    for (i = 0; i < 10; i++) { 
         sum += buf[i]; 
     } 
     return sum; 
} 

 

JNI支持一系列的Get/Release<Type>ArrayElement 函數,這些函數允許本地代碼獲取一個指向基本類型數組的元素的指針。

JNIEXPORT jint JNICALL  Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr) 
 { 
     jint *carr; 
     jint i, sum = 0; 
     carr = (*env)->GetIntArrayElements(env, arr, NULL); //推薦使用
     if (carr == NULL) { 
         return 0; /* exception occurred */ 
     } 
     for (i=0; i<10; i++) { 
         sum += carr[i]; 
     } 
     (*env)->ReleaseIntArrayElements(env, arr, carr, 0); 
     return sum; 
 }

如果你想在一個預先分配的C緩沖區和內存之間交換數據,應該使用Get/Set</Type>ArrayRegion系列函數。這些函數會進行越界檢查,在需要的時候會有可能拋出ArrayIndexOutOfBoundsException異常。
對於少量的、固定大小的數組,Get/Set<Type>ArrayRegion是最好的選擇,因為C緩沖區可以在Stack(棧)上被很快地分配,而且復制少量數組元素的代價是很小的。這對函數的另外一個優點就是,允許你通過傳入一個索引和長度來實現對子字符串的操作。

如果你沒有一個預先分配的 C 緩沖區,並且原始數組長度未定,而本地代碼又不想在獲取數組元素的指針時阻塞的話,使用 Get/ReleasePrimitiveArrayCritical 函數對。就像Get/ReleaseStringCritical函數對一樣,這對函數很小心地使用,以避免死鎖。

Get/Release<type>ArrayElements 系列函數永遠是安全的。JVM 會選擇性地返回一個指針,這個指針可能指向原始數據也可能指向原始數據復制。

 

對象數組:
JNI提供了一個函數對來訪問對象數組。GetObjectArrayElement返回數組中指定位置的元素,而SetObjectArrayElement修改數組中指定位置的元素。
與基本類型的數組不同的是,你不能一次得到所有的對象元素或者一次復制多個對象元素。
字符串和數組都是引用類型,你要使用Get/SetObjectArrayElement來訪問字符串數組或者數組的數組。

下面的例子調用了一個本地方法來創建一個二維的 int數組,然后打印這個數組的內容:

class ObjectArrayTest { 
     private static native int[][] initInt2DArray(int size); 
     public static void main(String[] args) { 
         int[][] i2arr = initInt2DArray(3); 
         for (int i = 0; i < 3; i++) { 
             for (int j = 0; j < 3; j++) { 
                  System.out.print(" " + i2arr[i][j]); 
             } 
             System.out.println(); 
         } 
     } 
     static { 
         System.loadLibrary("ObjectArrayTest"); 
     } 
 } 

靜態本地方法 initInt2DArray 創建了一個給定大小的二維數組。執行分配和初始化數組任務的本地方法可以是下面這樣子的:

JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass cls, int size) 
{ 
     jobjectArray result; 
     int i; 
     jclass intArrCls = (*env)->FindClass(env, "[I"); 
     if (intArrCls == NULL) { 
         return NULL; /* exception thrown */ 
     } 
     
     result = (*env)->NewObjectArray(env, size, intArrCls,  NULL); //分配第一維
     
     if (result == NULL) { 
         return NULL; /* out of memory error thrown */ 
     } 
     
     for (i = 0; i < size; i++) { 
         jint tmp[256];  /* make sure it is large enough! */ 
         int j; 
         jintArray iarr = (*env)->NewIntArray(env, size); //創建第二維數據
         if (iarr == NULL) { 
             return NULL; /* out of memory error thrown */ 
         } 
         for (j = 0; j < size; j++) { 
             tmp[j] = i + j; 
         } 
         (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); 
         (*env)->SetObjectArrayElement(env, result, i, iarr); 
         (*env)->DeleteLocalRef(env, iarr); 
     } 
     return result; 
 } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM