基礎(chǔ)數(shù)據(jù)類型

開發(fā)者使用JNI時最常問到的是JAVA和C/C++之間如何傳遞數(shù)據(jù),以及數(shù)據(jù)類型之間如何互相映射。本章我們從整數(shù)等基本類型和數(shù)組、字符串等普通的對象類型開始講述。至于如何傳遞任意對象,我們將在下一章中進(jìn)行講述。

3.1 一個簡單的本地方法

JAVA端源代碼如下:

class Prompt {
 // native method that prints a prompt and reads a line
 private native String getLine(String prompt);

 public static void main(String args[]) {
     Prompt p = new Prompt();
     String input = p.getLine("Type a line: ");
     System.out.println("User typed: " + input);
 }
 static {
     System.loadLibrary("Prompt");
 }
 }

3.1.1 本地方法的C函數(shù)原型

Prompt.getLine方法可以用下面這個C函數(shù)來實現(xiàn):

JNIEXPORT jstring JNICALL 
 Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt);

其中,JNIEXPORT和JNICALL這兩個宏(被定義在jni.h)確保這個函數(shù)在本地庫外可見,并且C編譯器會進(jìn)行正確的調(diào)用轉(zhuǎn)換。C函數(shù)的名字構(gòu)成有些講究,在11.3中會有一個詳細(xì)的解釋。

3.1.2 本地方法參數(shù)

第一個參數(shù)JNIEnv接口指針,指向一個個函數(shù)表,函數(shù)表中的每一個入口指向一個JNI函數(shù)。本地方法經(jīng)常通過這些函數(shù)來訪問JVM中的數(shù)據(jù)結(jié)構(gòu)。圖3.1演示了JNIEnv這個指針:


圖3.1 JNIEnv接口指針
第二個參數(shù)根據(jù)本地方法是一個靜態(tài)方法還是實例方法而有所不同。本地方法是一個靜態(tài)方法時,第二個參數(shù)代表本地方法所在的類;本地方法是一個實例方法時,第二個參數(shù)代表本地方法所在的對象。我們的例子當(dāng)中,Java_Prompt_getLine是一個實例方法,因此jobject參數(shù)指向方法所在的對象。

3.1.3 類型映射
本地方法聲明中的參數(shù)類型在本地語言中都有對應(yīng)的類型。JNI定義了一個C/C++類型的集合,集合中每一個類型對應(yīng)于JAVA中的每一個類型。
JAVA中有兩種類型:基本數(shù)據(jù)類型(int,float,char等)和引用類型(類,對象,數(shù)組等)。
JNI對基本類型和引用類型的處理是不同的。基本類型的映射是一對一的。例如JAVA中的int類型直接對應(yīng)C/C++中的jint(定義在jni.h中的一個有符號 32位整數(shù))。12.1.1包含了JNI中所有基本類型的定義。
JNI把JAVA中的對象當(dāng)作一個C指針傳遞到本地方法中,這個指針指向JVM中的內(nèi)部數(shù)據(jù)結(jié)構(gòu),而內(nèi)部數(shù)據(jù)結(jié)構(gòu)在內(nèi)存中的存儲方式是不可見的。本地代碼必須通過在JNIEnv中選擇適當(dāng)?shù)腏NI函數(shù)來操作JVM中的對象。例如,對于java.lang.String對應(yīng)的JNI類型是jstring,但本地代碼只能通過GetStringUTFChars這樣的JNI函數(shù)來訪問字符串的內(nèi)容。
所有的JNI引用都是jobject類型,對了使用方便和類型安全,JNI定義了一個引用類型集合,集合當(dāng)中的所有類型都是jobject的子類型。這些子類型和JAVA中常用的引用類型相對應(yīng)。例如,jstring表示字符串,jobjectArray表示對象數(shù)組。

3.2 訪問字符串

Java_Prompt_getLine接收一個jstring類型的參數(shù)prompt,jstring類型指向JVM內(nèi)部的一個字符串,和常規(guī)的C字符串類型char*不同。你不能把jstring當(dāng)作一個普通的C字符串。

3.2.1 轉(zhuǎn)換為本地字符串

本地代碼中,必須使用合適的JNI函數(shù)把jstring轉(zhuǎn)化為C/C++字符串。JNI支持字符串在Unicode和UTF-8兩種編碼之間轉(zhuǎn)換。Unicode字符串代表了16-bit的字符集合。UTF-8字符串使用一種向上兼容7-bit ASCII字符串的編碼協(xié)議。UTF-8字符串很像NULL結(jié)尾的C字符串,在包含非ASCII字符的時候依然如此。所有的7-bitASCII字符的值都在1~127之間,這些值在UTF-8編碼中保持原樣。一個字節(jié)如果最高位被設(shè)置了,意味著這是一個多字節(jié)字符(16-bitUnicode值)。
函數(shù)Java_Prompt_getLine通過調(diào)用JNI函數(shù)GetStringUTFChars來讀取字符串的內(nèi)容。GetStringUTFChars可以把一個jstring指針(指向JVM內(nèi)部的Unicode字符序列)轉(zhuǎn)化成一個UTF-8格式的C字符串。如何你確信原始字符串?dāng)?shù)據(jù)只包含7-bit ASCII字符,你可以把轉(zhuǎn)化后的字符串傳遞給常規(guī)的C庫函數(shù)使用,如printf。我們會在8.2中討論如何處理非ASCII字符串。

JNIEXPORT jstring JNICALL 
 Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
 {
 char buf[128];
 const jbyte *str;
 str = (*env)->GetStringUTFChars(env, prompt, NULL);
 if (str == NULL) {
     return NULL; /* OutOfMemoryError already thrown */
 }
 printf("%s", str);
 (*env)->ReleaseStringUTFChars(env, prompt, str);
 /* We assume here that the user does not type more than
  * 127 characters */
 scanf("%s", buf);
 return

不要忘記檢查GetStringUTFChars。因為JVM需要為新誕生的UTF-8字符串分配內(nèi)存,這個操作有可能因為內(nèi)存太少而失敗。失敗時,GetStringUTFChars會返回NULL,并拋出一個OutOfMemoryError異常(對異常的處理在第6章)。這些JNI拋出的異常與JAVA中的異常是不同的。一個由JNI拋出的未決的異常不會改變程序執(zhí)行流,因此,我們需要一個顯示的return語句來跳過C函數(shù)中的剩余語句。Java_Prompt_getLine函數(shù)返回后,異常會在Prompt.main(Prompt.getLine這個發(fā)生異常的函數(shù)的調(diào)用者)中拋出,
3.2.2 釋放本地字符串資源
從GetStringUTFChars中獲取的UTF-8字符串在本地代碼中使用完畢后,要使用ReleaseStringUTFChars告訴JVM這個UTF-8字符串不會被使用了,因為這個UTF-8字符串占用的內(nèi)存會被回收。
3.2.3 構(gòu)造新的字符串
你可以通過JNI函數(shù)NewStringUTF在本地方法中創(chuàng)建一個新的java.lang.String字符串對象。這個新創(chuàng)建的字符串對象擁有一個與給定的UTF-8編碼的C類型字符串內(nèi)容相同的Unicode編碼字符串。
如果一個VM不能為構(gòu)造java.lang.String分配足夠的內(nèi)存,NewStringUTF會拋出一個OutOfMemoryError異常,并返回一個NULL。在這個例子中,我們不必檢查它的返回值,因為本地方法會立即返回。如果NewStringUTF失敗,OutOfMemoryError這個異常會被在Prompt.main(本地方法的調(diào)用者)中拋出。如果NeweStringUTF成功,它會返回一個JNI引用,這個引用指向新創(chuàng)建的java.lang.String對象。這個對象被Prompt.getLine返回然后被賦值給Prompt.main中的本地input。
3.2.4 其它JNI字符串處理函數(shù)
JNI支持許多操作字符串的函數(shù),這里做個大致介紹。
GetStringChars和ReleaseStringChars獲取以Unicode格式編碼的字符串。當(dāng)操作系統(tǒng)支持Unicode編碼的字符串時,這些方法很有用。
UTF-8字符串以’\0’結(jié)尾,而Unicode字符串不是。如果jstring指向一個Unicode編碼的字符串,為了得到這個字符串的長度,可以調(diào)用GetStringLength。如果一個jstring指向一個UTF-8編碼的字符串,為了得到這個字符串的字節(jié)長度,可以調(diào)用標(biāo)準(zhǔn)C函數(shù)strlen。或者直接對jstring調(diào)用JNI函數(shù)GetStringUTFLength,而不用管jstring指向的字符串的編碼格式。
GetStringChars和GetStringUTFChars函數(shù)中的第三個參數(shù)需要更進(jìn)一步的解釋:
const jchar *
GetStringChars(JNIEnv *env, jstring str, jboolean *isCopy);
當(dāng)從JNI函數(shù)GetStringChars中返回得到字符串B時,如果B是原始字符串java.lang.String的拷貝,則isCopy被賦值為JNI_TRUE。如果B和原始字符串指向的是JVM中的同一份數(shù)據(jù),則isCopy被賦值為JNI_FALSE。當(dāng)isCopy值為JNI_FALSE時,本地代碼決不能修改字符串的內(nèi)容,否則JVM中的原始字符串也會被修改,這會打破JAVA語言中字符串不可變的規(guī)則。
通常,因為你不必關(guān)心JVM是否會返回原始字符串的拷貝,你只需要為isCopy傳遞NULL作為參數(shù)。
JVM是否會通過拷貝原始Unicode字符串來生成UTF-8字符串是不可以預(yù)測的,程序員最好假設(shè)它會進(jìn)行拷貝,而這個操作是花費時間和內(nèi)存的。一個典型的JVM會在heap上為對象分配內(nèi)存。一旦一個JAVA字符串對象的指針被傳遞給本地代碼,GC就不會再碰這個字符串。換言之,這種情況下,JVM必須pin這個對象。可是,大量地pin一個對象是會產(chǎn)生內(nèi)存碎片的,因為,虛擬機會隨意性地來選擇是復(fù)制還是直接傳遞指針。
當(dāng)你不再使用一個從GetStringChars得到的字符串時,不管JVM內(nèi)部是采用復(fù)制還是直接傳遞指針的方式,都不要忘記調(diào)用ReleaseStringChars。根據(jù)方法GetStringChars是復(fù)制還是直接返回指針,ReleaseStringChars會釋放復(fù)制對象時所占的內(nèi)存,或者unpin這個對象。

3.2.5 JDK1.2中關(guān)于字符串的新JNI函數(shù)

為了提高JVM返回字符串直接指針的可能性,JDK1.2中引入了一對新函數(shù),Get/ReleaseStringCritical。表面上,它們和Get/ReleaseStringChars函數(shù)差不多,但實際上這兩個函數(shù)在使用有很大的限制。
使用這兩個函數(shù)時,你必須兩個函數(shù)中間的代碼是運行在"critical region"(臨界區(qū))的,即,這兩個函數(shù)中間的本地代碼不能調(diào)用任何會讓線程阻塞或等待JVM中的其它線程的本地函數(shù)或JNI函數(shù)。
有了這些限制, JVM就可以在本地方法持有一個從GetStringCritical得到的字符串的直接指針時禁止GC。當(dāng)GC被禁止時,任何線程如果觸發(fā)GC的話,都會被阻塞。而Get/ReleaseStringCritical這兩個函數(shù)中間的任何本地代碼都不可以執(zhí)行會導(dǎo)致阻塞的調(diào)用或者為新對象在JVM中分配內(nèi)存。否則,JVM有可能死鎖,想象一下這樣的場景中:
1、 只有當(dāng)前線程觸發(fā)的GC完成阻塞并釋放GC時,由其它線程觸發(fā)的GC才可能由阻塞中釋放出來繼續(xù)運行。
2、 在這個過程中,當(dāng)前線程會一直阻塞。因為任何阻塞性調(diào)用都需要獲取一個正被其它線程持有的鎖,而其它線程正等待GC。
Get/ReleaseStringCritical的交迭調(diào)用是安全的,這種情況下,它們的使用必須有嚴(yán)格的順序限制。而且,我們一定要記住檢查是否因為內(nèi)存溢出而導(dǎo)致它的返回值是NULL。因為JVM在執(zhí)行GetStringCritical這個函數(shù)時,仍有發(fā)生數(shù)據(jù)復(fù)制的可能性,尤其是當(dāng)JVM內(nèi)部存儲的數(shù)組不連續(xù)時,為了返回一個指向連續(xù)內(nèi)存空間的指針,JVM必須復(fù)制所有數(shù)據(jù)。
總之,為了避免死鎖,在Get/ReleaseStringCritical之間不要調(diào)用任何JNI函數(shù)。Get/ReleaseStringCritical和 Get/ReleasePrimitiveArrayCritical這兩個函數(shù)是可以的。
下面代碼演示了這對函數(shù)的正確用法:

jchar *s1, *s2;
 s1 = (*env)->GetStringCritical(env, jstr1);
 if (s1 == NULL) {
 ... /* error handling */
 }
 s2 = (*env)->GetStringCritical(env, jstr2);
 if (s2 == NULL) {
 (*env)->ReleaseStringCritical(env, jstr1, s1);
 ... /* error handling */
 }
 ...     /* use s1 and s2 */
 (*env)->ReleaseStringCritical(env, jstr1, s1);
 (*env)->ReleaseStringCritical(env, jstr2, s2);

JNI不支持Get/ReleaseStringUTFCritical,因為這樣的函數(shù)在進(jìn)行編碼轉(zhuǎn)換時很可能會促使JVM對數(shù)據(jù)進(jìn)行復(fù)制,因為JVM內(nèi)部表示字符串一般都是使用Unicode的。
JDK1.2還一對新增的函數(shù):GetStringRegion和GetStringUTFRegion。這對函數(shù)把字符串復(fù)制到一個預(yù)先分配的緩沖區(qū)內(nèi)。Prompt.getLine這個本地方法可以用GetStringUTFRegion重新實現(xiàn)如下:

JNIEXPORT jstring JNICALL 
 Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
 {
 /* assume the prompt string and user input has less than 128
    characters */
 char outbuf[128], inbuf[128];
 int len = (*env)->GetStringLength(env, prompt);
 (*env)->GetStringUTFRegion(env, prompt, 0, len, outbuf);
 printf("%s", outbuf);
 scanf("%s", inbuf);
 return (*env)->NewStringUTF(env, inbuf);
 }

GetStringUTFRegion這個函數(shù)會做越界檢查,如果必要的話,會拋出異常StringIndexOutOfBoundsException。這個方法與GetStringUTFChars比較相似,不同的是,GetStringUTFRegion不做任何內(nèi)存分配,不會拋出內(nèi)存溢出異常。

3.2.6 JNI字符串操作函數(shù)總結(jié)

對于小字符串來說,Get/SetStringRegion和Get/SetString-UTFRegion這兩對函數(shù)是最佳選擇,因為緩沖區(qū)可以被編譯器提前分配,而且永遠(yuǎn)不會產(chǎn)生內(nèi)存溢出的異常。當(dāng)你需要處理一個字符串的一部分時,使用這對函數(shù)也是不錯的,因為它們提供了一個開始索引和子字符串的長度值。另外,復(fù)制少量字符串的消耗是非常小的。
在使用GetStringCritical時,必須非常小心。你必須確保在持有一個由GetStringCritical獲取到的指針時,本地代碼不會在JVM內(nèi)部分配新對象,或者做任何其它可能導(dǎo)致系統(tǒng)死鎖的阻塞性調(diào)用。
下面的例子演示了使用GetStringCritical時需要注意的一些地方:

/* This is not safe! */
 const char *c_str = (*env)->GetStringCritical(env, j_str, 0);
 if (c_str == NULL) {
 ... /* error handling */
 }
 fprintf(fd, "%s\n", c_str);
(*env)->ReleaseStringCritical(env, j_str, c_str);

上面代碼的問題在于,GC被當(dāng)前線程禁止的情況下,向一個文件寫數(shù)據(jù)不一定安全。例如,另外一個線程T正在等待從文件fd中讀取數(shù)據(jù)。假設(shè)操作系統(tǒng)的規(guī)則是fprintf會等待線程T完成所有對文件fd的數(shù)據(jù)讀取操作,這種情況下就可能會產(chǎn)生死鎖:線程T從文件fd中讀取數(shù)據(jù)是需要緩沖區(qū)的,如果當(dāng)前沒有足夠內(nèi)存,線程T就會請求GC來回收一部分,GC一旦運行,就只能等到當(dāng)前線程運行ReleaseStringCritical時才可以。而ReleaseStringCritical只有在fprintf調(diào)用返回時才會被調(diào)用。而fprintf這個調(diào)用,會一直等待線程T完成文件讀取操作。

3.3 訪問數(shù)組

JNI在處理基本類型數(shù)組和對象數(shù)組上面是不同的。對象數(shù)組里面是一些指向?qū)ο髮嵗蛘咂渌鼣?shù)組的引用。
本地代碼中訪問JVM中的數(shù)組和訪問JVM中的字符串有些相似。看一個簡單的例子。下面的程序調(diào)用了一個本地方法sumArray,這個方法對一個int數(shù)組里面的元素進(jìn)行累加:

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

3.3.1 在本地代碼中訪問數(shù)組

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

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

3.3.2 訪問基本類型數(shù)組

上一個例子中,使用GetIntArrayRegion函數(shù)來把一個int數(shù)組中的所有元素復(fù)制到一個C緩沖區(qū)中,然后我們在本地代碼中通過C緩沖區(qū)來訪問這些元素。
JNI支持一個與GetIntArrayRegion相對應(yīng)的函數(shù)SetIntArrayRegion。這個函數(shù)允許本地代碼修改所有的基本類型數(shù)組中的元素。
JNI支持一系列的Get/Release<Type>ArrayElement函數(shù),這些函數(shù)允許本地代碼獲取一個指向基本類型數(shù)組的元素的指針。由于GC可能不支持pin操作,JVM可能會先對原始數(shù)據(jù)進(jìn)行復(fù)制,然后返回指向這個緩沖區(qū)的指針。我們可以重寫上面的本地方法實現(xiàn):

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

GetArrayLength這個函數(shù)返回數(shù)組中元素的個數(shù),這個值在數(shù)組被首次分配時確定下來。
JDK1.2引入了一對函數(shù):Get/ReleasePrimitiveArrayCritical。通過這對函數(shù),可以在本地代碼訪問基本類型數(shù)組元素的時候禁止GC的運行。但程序員使用這對函數(shù)時,必須和使用Get/ReleaseStringCritical時一樣的小心。在這對函數(shù)調(diào)用的中間,同樣不能調(diào)用任何JNI函數(shù),或者做其它可能會導(dǎo)致程序死鎖的阻塞性操作。

3.3.3 操作基本類型數(shù)組的JNI函數(shù)的總結(jié)

如果你想在一個預(yù)先分配的C緩沖區(qū)和內(nèi)存之間交換數(shù)據(jù),應(yīng)該使用Get/Set</Type>ArrayRegion系列函數(shù)。這些函數(shù)會進(jìn)行越界檢查,在需要的時候會有可能拋出ArrayIndexOutOfBoundsException異常。
對于少量的、固定大小的數(shù)組,Get/Set<Type>ArrayRegion是最好的選擇,因為C緩沖區(qū)可以在Stack(棧)上被很快地分配,而且復(fù)制少量數(shù)組元素的代價是很小的。這對函數(shù)的另外一個優(yōu)點就是,允許你通過傳入一個索引和長度來實現(xiàn)對子字符串的操作。
如果你沒有一個預(yù)先分配的C緩沖區(qū),并且原始數(shù)組長度未定,而本地代碼又不想在獲取數(shù)組元素的指針時阻塞的話,使用Get/ReleasePrimitiveArrayCritical函數(shù)對。就像Get/ReleaseStringCritical函數(shù)對一樣,這對函數(shù)很小心地使用,以避免死鎖。
Get/Release<type>ArrayElements系列函數(shù)永遠(yuǎn)是安全的。JVM會選擇性地返回一個指針,這個指針可能指向原始數(shù)據(jù)也可能指向原始數(shù)據(jù)復(fù)制。

3.3.5 訪問對象數(shù)組

JNI提供了一個函數(shù)對來訪問對象數(shù)組。GetObjectArrayElement返回數(shù)組中指定位置的元素,而SetObjectArrayElement修改數(shù)組中指定位置的元素。與基本類型的數(shù)組不同的是,你不能一次得到所有的對象元素或者一次復(fù)制多個對象元素。字符串和數(shù)組都是引用類型,你要使用Get/SetObjectArrayElement來訪問字符串?dāng)?shù)組或者數(shù)組的數(shù)組。
下面的例子調(diào)用了一個本地方法來創(chuàng)建一個二維的int數(shù)組,然后打印這個數(shù)組的內(nèi)容:

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

靜態(tài)本地方法initInt2DArray創(chuàng)建了一個給定大小的二維數(shù)組。執(zhí)行分配和初始化數(shù)組任務(wù)的本地方法可以是下面這樣子的:

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

函數(shù)newInt2DArray首先調(diào)用JNI函數(shù)FindClass來獲得一個int型二維數(shù)組類的引用,傳遞給FindClass的參數(shù)“[I”是JNI class descriptor(JNI類型描述符),它對應(yīng)著JVM中的int[]類型。如果類加載失敗的話,F(xiàn)indClass會返回NULL,然后拋出一個異常。
接下來,NewObjectArray會分配一個數(shù)組,這個數(shù)組里面的元素類型用intArrCls類引用來標(biāo)識。函數(shù)NewObjectArray只能分配第一維,JVM沒有與多維數(shù)組相對應(yīng)的數(shù)據(jù)結(jié)構(gòu)。一個二維數(shù)組實際上就是一個簡單的數(shù)組的數(shù)組。
創(chuàng)建第二維數(shù)據(jù)的方式非常直接,NewInt-Array為每個數(shù)組元素分配空間,然后SetIntArrayRegion把tmp[]緩沖區(qū)中的內(nèi)容復(fù)制到新分配的一維數(shù)組中去。
在循環(huán)最后調(diào)用DeleteLocalRef,確保JVM釋放掉iarr這個JNI引用。

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

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