天天看點

Android NDK開發系列教程2:基本方法調用及傳參

有時候我寫了個Java層的方法,希望native層也能夠調用(尤其是一個實體類的get,set方法在native一般都會用到)。這在jni開發中也很常見,jni.h中也提供了很多方法。下面利用具體執行個體進行說明。這裡直接使用AS3.0裡面的CMake進行編譯了,之後會講解下Android.mk和Application.mk的用法和含義。這裡我主要介紹一下幾個:

1. java向native傳遞常用基本資料類型和字元串類型

2. java向native傳遞數組類型

3. java向native傳遞自定義java對象

4. java向native傳遞List對象

5. native向java傳回字元串類型

6. native向java傳回java對象

7. native向java傳回數組類型

8. native向Java傳回List對象

對于上面的每個都給出對應的例子。

本節所有案例代碼均已放到GitHub上,歡迎下載下傳:

<a href="https://github.com/huangtianyu/JNILearnCourse">https://github.com/huangtianyu/JNILearnCourse</a>

在我們調用方法時會用到方法的簽名,使用類變量時需要用該變量對應的jni類型。下面給出對應的類型對照表。

1. 基本資料類型對照表:

Android NDK開發系列教程2:基本方法調用及傳參

2. 對象類型對照表:

Android NDK開發系列教程2:基本方法調用及傳參

3. 簡寫對應表

Android NDK開發系列教程2:基本方法調用及傳參

強大的AS在你寫了java的native方法後,直接快捷鍵按Alt+Enter後即可生成對應的方法。

java層的方法:

生成後的native方法:

在上面可以看到,Java層的基本類型方法都會經過jni進行轉換,轉換成相應的jni類型。其操作也很友善。Java的String類型需要注意下,一般是将jstring先轉換為char*然後對char *進行操作。由于這擷取了一個局部引用,一般在調用結束後需要釋放該局部引用。

對應的jni方法是:

其中函數: jsize GetArrayLength(jxxxarray array);用于擷取數組的長度

在Java端調用代碼如下:

(1) GetXXXArrayElements(Array arr , jboolean* isCopide);

這類函數可以把Java基本類型的數組轉換到C/C++中的數組,有兩種處理方式,一種JNI_TRUE是拷貝一份傳回本地代碼,另一個是JNI_FALSE把指向Java數組的指針直接傳回到本地代碼中,處理完本地化的數組後,通過ReleaseXXXArrayElements來釋放數組

(2) ReleaseXXXArrayElements(Array arr , * array , jint mode)

用這個函數可以選擇将如何處理Java跟C++的數組,是送出,還是撤銷等,記憶體釋放還是不釋放等mode可以取下面的值:

0 :對Java的數組進行更新并釋放C/C++的數組

JNI_COMMIT :對Java的數組進行更新但是不釋放C/C++的數組

JNI_ABORT:對Java的數組不進行更新,釋放C/C++的數組

(3) GetPrimitiveArrayCritical(jarray arr , jboolean* isCopied);

在獲得數組上的鎖後将傳回一個句柄給數組。如果沒有建立任何鎖,則isCopy被置為JNI_TRUE,否則置為NULL或JNI_FALSE:

(4) ReleasePrimitiveArrayCritical(jarray arr , void* array , jint mode);

釋放從GetPrimitiveArrayCritical調用中傳回的數組。也是JDK1.2出來的,為了增加直接傳回指向Java數組的指針而加入的函數,同樣的也會有同GetStringCritical的死鎖的問題。mode取值如下:

0:從carray中複制值到數組中,并釋放配置設定給carray的存儲器

JNI_COMMIT:從carray中複制值到數組中,但是不釋放配置設定給carray的存儲器

JNI_ABORT:不從carray中複制值到數組中

(5) GetXXXArrayRegion(Array arr , jsize start , jsize len , * buffer);

在C/C++預先開辟一段記憶體,然後把Java基本類型的數組拷貝到這段記憶體中。用于一個數組子集的複制操作。參數start指定了從何處複制的起始索引,參數len則指定了從數組中複制到本機數組的多個位置數量。

(6) SetXXXArrayRegion(Array arr , jsize start , jsize len , const * buffer);

用來複制本機數組的一段内容回Java數組中。元素一般從本機數組起始處(索引為0)開始複制,但是隻是從位置start開始将len個元素複制到Java數組中。

(7) Array NewXXXArray(jsize sz)

建立一個包含length個元素的Java數組。

(1) jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement );

建立對象數組,建立一個長度為length,并且持有類型為elementClass的對象的對象數組,數組中的所有元素都被置為initialElement

(2) jobject GetObjectArrayElement(jobjectArray array, jsize Index);

擷取數組元素,通過Index指定的索引在array中擷取一個對象,如果索引超出邊界,會抛出一個IndexOutOfBoundsException

(3) void SetObjectArrayElement(jobjectArray array, jsize index,jobject value);

設定元素值。在array中通過index指定的索引處設定元素值為value,如果index超出邊界,會抛出一個IndexOutOfBoundException。

定義一個Java層方法:

定義一個Java的native方法:

在native層實作

在Java端調用

從Java端傳對象執行個體給native時,到native端任何對象都變為jobject類型,如果要做對該對象執行個體的任何操作需先擷取該對象的jfieldID ,jmethodID,然後通過env-&gt;CallXXXMethod來操作該對象的方法其中第一個參數是該對象的具體執行個體,其中env-&gt;CallStaticXXXMethod方法用來調用該類的靜态方法,調用靜态方法的時候就不用傳具體的對象過去了。

定義Java的native方法

在native中實作具體方法:

<code>jclass cls = env-&gt;GetObjectClass(people);</code>這是擷取一個對象執行個體相應的類的最好的辦法。從上面可以看出List在傳到native時也是變成了jobject,然後具體操作都得通過env-&gt;GetObjectClass先擷取到該類,然後擷取到該類的具體jmethodID,jfieldID來完成相應的操作。調用的方法也是env-&gt;CallXXXMethod()。

然後在Java端調用該native方法:

在上面即可通過native将Person的name全部進行了更改。

<b>上面</b>都是Java向native傳參,基本用法都類似。基本資料類型有相應的對照表,對象類型的都轉為jobject,對對象的操作都是先擷取該對象jclass,jmethodID,jfielID後再對對象執行個體進行操作。

在JNI裡面的方法有很多,記起來也較為麻煩。當有不會用時,可以參考以下手冊,裡面翻譯了JNI常用的方法。

<a href="http://www.ceeger.com/Script/AndroidJNI/AndroidJNI.html">http://www.ceeger.com/Script/AndroidJNI/AndroidJNI.html</a>