上篇文章JNI基本類型數組操做講述瞭如何在JNI層操做基本類型的數組,本片文章承繼上篇文章來說解如何在JNI層操做引用類型的數組。java
若是在閱讀本文的時候,你發現有不懂的操做,而我又沒有詳細解釋的,能夠從下面幾篇文章中尋找答案c++
在講解以前呢,須要作一些準備工做。數組
首先須要一個Java引用類型的類Person.java
,這個類將被用作建立數組函數
package com.uni.ndkdemo;
public class Person {
private String mName;
public Person(String name) {
mName = name;
}
public void sayHello() {
System.out.println("Hello, " + mName + "!");
}
}
複製代碼
而後得準備一個native
入口的類post
package com.uni.ndkdemo;
public class ArrayTest {
static {
System.loadLibrary("array_jni");
}
public native void sayHello(Person[] persons);
public static void main(String[] args) {
ArrayTest arrayTest = new ArrayTest();
Person persons[] = new Person[2];
persons[0] = new Person("Jay Chow");
persons[1] = new Person("Stephen Chow");
arrayTest.sayHello(persons);
}
}
複製代碼
ArrayTest.java
的main()
方法中建立一個了Person
數組,並給每一個元素賦值,而後調用了sayHello()
這個native
方法把這Person
數組傳入JNI層。ui
最後,經過函數動態註冊,須要在JNI層實現對應的函數,假設這個函數以下spa
static void com_uni_ndkdemo_ArrayTest_sayHello(JNIEnv *env, jobject thiz, jobjectArray objectArray) {
}
複製代碼
我將在這個函數中來說解JNI如何操做引用類型數組。code
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);
複製代碼
GetObjectArrayElement
函數獲取數組array
在索引index
下的元素。對象
注意,這個函數只能用於獲取引用類型數組的元素,對於如何獲取基本類型數組的元素,請參考JNI基本類型數組操做。索引
jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);
複製代碼
IsInstanceOf
函數用於判斷對象obj
是不是clazz
類的實例對象。
能夠利用這個函數來判斷數組元素的類型,這樣就能夠避免調用了錯的方法。
static void com_uni_ndkdemo_ArrayTest_sayHello(JNIEnv *env, jobject thiz, jobjectArray objectArray) {
// 1. 獲取數組的長度
jsize length = env->GetArrayLength(objectArray);
// 2. 獲取Person類的Class對象
jclass clazz_Person = env->FindClass("com/uni/ndkdemo/Person");
if (clazz_Person == NULL)
{
return;
}
// 3. 獲取Person的sayHello方法字段
jmethodID methodID_sayHello = env->GetMethodID(clazz_Person, "sayHello", "()V");
if (methodID_sayHello == NULL)
{
return;
}
// 4. 循環調用每一個Person對象的sayHello()方法
for (int i = 0; i < length; i++)
{
// 獲取引用類型數組的對象
jobject element = env->GetObjectArrayElement(objectArray, i);
// 判斷數組元素是不是Person類對象
if (env->IsInstanceOf(element, clazz_Person))
{
// 調用Person對象的sayHello()方法
env->CallVoidMethod(element, methodID_sayHello);
}
}
}
複製代碼
首先經過第二步和第三步來獲取Person
類的sayHello()
方法的jmethodID
,而後調用GetObjectArrayElement
來獲取數組的元素,最後經過IsInstanceOf
判斷數組元素是Person
類對象後,就調用了它的sayHello
方法。
前面所講的都是處理從Java層傳入JNI層的引用類型數組,固然,也能夠在JNI層建立引用類型數組,並返回給Java層,這就要用到NewObjectArray
函數
jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
複製代碼
NewObjectArray
會根據參數elementClass
的類型,建立一個長度爲length
的數組。
若是你指定了第四個參數initialElement
,那麼將會用第四個參數初始化數組的全部元素。若是指定initialElement
爲NULL
,那麼數組全部元素爲NULL
。
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
複製代碼
SetObjectArrayElement
函數是爲數組array
,設置索引index
下的元素的值value
。
NewObjectArray
能夠在建立數組的時候,用參數jobject initialElement
給數組每一個元素賦初值。SetObjectArrayElement
函數能夠用參數jobject value
給數組元素設置值。那麼問題來了,這個用於賦值的對象如何建立呢?可使用 NewObject
/NewObjectA
/NewObjectV
,或者 AllocObject
函數。
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
複製代碼
這三個函數的區別在於傳入參數的形式不同而已。
參數clazz
表明Java類的Class
對象,能夠經過FindClass
函數來獲取。
參數methodID
表明Java類的構造函數,須要經過GetMethodID
來獲取,不過調用GetMethodID
函數時,傳入的函數名參數必定要爲<init>
,並且傳入函數簽名參數的返回值必定要爲V
。這個可能說的有點抽象,不過能夠從後面的例子中看出如何使用。
jobject AllocObject(JNIEnv *env, jclass clazz);
複製代碼
AllocObject
函數很是有意思,它會爲clazz
類的對象分配內存,而不用調用clazz
所指定類的任何構造函數,並返回一個指向這個對象的引用。
AllocObject
函數只是爲對象分配內存,可是並無給對象的變量賦值,所以須要咱們手動對給對象的變量進行賦值。而前面講的NewObject
函數須要指定調用哪一個構造函數,而且若是構造函數帶有參數,還得在調用的時候傳入相應的參數。
如今須要先準備一個Java的native
方法,返回一個Person
數組
public class ArrayTest {
static {
System.loadLibrary("array_jni");
}
public native Person[] getNativePersons(int length);
public static void main(String[] args) {
for (int i = 0; i < nativePersons.length; i++) {
nativePersons[i].sayHello();
}
}
}
複製代碼
而後到JNI層去實現
static const int NAME_SIZE = 2;
static const char *names[NAME_SIZE] = {"周星馳", "周杰倫"};
static jobjectArray getPersons(JNIEnv *env, jobject thiz, jint length) {
// 1. 找到Person類的Class對象
jclass clazz_Person = env->FindClass("com/umx/ndkdemo/Person");
if (clazz_Person == NULL) {
return NULL;
}
// 2. 找到Person類的構造函數的jmethodID
// 注意,這裏找的是帶有String參數的構造函數
jmethodID methodId_Person_constructor = env->GetMethodID(clazz_Person, "<init>",
"(Ljava/lang/String;)V");
if (methodId_Person_constructor == NULL) {
return NULL;
}
// 3. 建立一個Person數組,每一個元素爲NULL
jobjectArray array = env->NewObjectArray(length, clazz_Person, NULL);
// 4. 循環爲數組中的每一個元素賦值
for (int i = 0; i < length; i++) {
const char *name = names[i % NAME_SIZE];
jobject person = NULL;
if (i == length - 1) {
// 爲Person對象分配內存
person = env->AllocObject(clazz_Person);
jmethodID methodID_setName = env->GetMethodID(clazz_Person, "setName",
"(Ljava/lang/String;)V");
// 調用setName()方法給建立的Person對象的mName變量賦值
env->CallVoidMethod(person, methodID_setName, env->NewStringUTF(name));
} else {
// 調用Person(String name)構造函數建立Person對象
person = env->NewObject(clazz_Person, methodId_Person_constructor,
env->NewStringUTF(name));
}
// 給數組元素設置值
env->SetObjectArrayElement(array, i, person);
// 釋放局部變量
env->DeleteLocalRef(person);
}
return array;
}
複製代碼
第三步調用NewObjectArray
來建立Person
數組,可是因爲最後一個參數爲NULL
,所以數組元素的值都爲NULL
。那麼就須要第四步來爲數組每一個元素賦值。
在第四步的實現中,若是是數組中最後一個元素,那麼就調用AllocObject
函數來爲Person
對象分配內存,可是這個對象尚未數據,必須經過調用setName
方法來設置mName
的值。而數組中其它元素都是調用Person(String name)
這個構造函數來建立對象的,因此在調用NewObject
的時候得傳入一個參數。
對象建立好了以後就須要爲調用SetObjectArrayElement
來爲每一個元素賦值。
若是這個實現中還有不懂的地方,請參考文章開頭前指定的文章。
JNI處理引用類型的數組要比處理基本類型數組要複雜得多,由於要涉及到不少的方方面面,可是這些方方面面都在前面的文章中講過。全部,若是你看這篇文章有困惑,不妨去前面的文章中尋找答案。
Java的String
類也是一個引用類型,可是這個引用類型是用數組實現的,所以比較特殊。在下一篇文章中,我將會來說解JNI如何來操做字符串的。