有時候我寫了個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. 基本資料類型對照表:
2. 對象類型對照表:
3. 簡寫對應表
強大的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->CallXXXMethod來操作該對象的方法其中第一個參數是該對象的具體執行個體,其中env->CallStaticXXXMethod方法用來調用該類的靜态方法,調用靜态方法的時候就不用傳具體的對象過去了。
定義Java的native方法
在native中實作具體方法:
<code>jclass cls = env->GetObjectClass(people);</code>這是擷取一個對象執行個體相應的類的最好的辦法。從上面可以看出List在傳到native時也是變成了jobject,然後具體操作都得通過env->GetObjectClass先擷取到該類,然後擷取到該類的具體jmethodID,jfieldID來完成相應的操作。調用的方法也是env->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>