Android 還能夠經過 JNI 來調用 Java 一個類的構造方法,從而建立一個 Java 類。java
調用構造方法的步驟和以前調用類的實例方法步驟相似,也須要得到對應的類和方法 id。git
對於類,經過 FindClass 能夠找到對應的 Java 類型。github
對於構造方法,它的方法 id 仍是經過 GetMethodID
方法來得到,可是構造方法對應的名稱爲 <init>,返回值類型是 void 類型的。數組
完成了以上準備條件後,就能夠經過 NewObject
來調用構造方法,從而建立具體的類。微信
下面以 String 的某個構造方法爲例函數
public String(char value[]) // Java String 類的其中一個構造方法
對應的 C++ 代碼:this
extern "C" JNIEXPORT jstring JNICALL Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_invokeStringConstructors(JNIEnv *env, jobject instance) { jclass stringClass; jmethodID cid; jcharArray elemArr; jstring result; // 由 C++ 字符串建立一個 Java 字符串 jstring temp = env->NewStringUTF("this is char array"); // 再從 Java 字符串得到一個字符數組指針,做爲 String 構造函數的參數 const jchar *chars = env->GetStringChars(temp, NULL); int len = 10; stringClass = env->FindClass("java/lang/String"); // 找到具體的 String 類 if (stringClass == NULL) { return NULL; } // 找到具體的方法,([C)V 表示選擇 String 的 String(char value[]) 構造方法 cid = env->GetMethodID(stringClass, "<init>", "([C)V"); if (cid == NULL) { return NULL; } // 字符串數組做爲參數 elemArr = env->NewCharArray(len); if (elemArr == NULL) { return NULL; } // 給字符串數組賦值 env->SetCharArrayRegion(elemArr, 0, len, chars); // 建立類 result = (jstring) env->NewObject(stringClass, cid, elemArr); env->DeleteLocalRef(elemArr); env->DeleteLocalRef(stringClass); return result; }
因爲 String 的構造函數須要傳遞一個字符數組,就先構造好了字符數組並賦值,獲得對應的類和方法 id 以後,直接經過 NewObject
方法調用便可。spa
再來看一個調用自定義類的構造方法的示例,仍是以前的 Animal 類,它的構造方法有一個 String 類型的參數。3d
/** * 建立一個 Java 的 Animal 類並返回 */ extern "C" JNIEXPORT jobject JNICALL Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_invokeAnimalConstructors(JNIEnv *env, jobject instance) { jclass animalClass; jmethodID mid; jobject result; animalClass = env->FindClass("com/glumes/cppso/model/Animal"); if (animalClass == NULL) { return NULL; } mid = env->GetMethodID(animalClass, "<init>", "(Ljava/lang/String;)V"); if (mid == NULL) { return NULL; } jstring args = env->NewStringUTF("this animal name"); result = env->NewObject(animalClass, mid, args); env->DeleteLocalRef(animalClass); return result; }
能夠看到,整個調用流程只要按照步驟來,就能夠了。指針
除了 NewObject 方法以外,JNI 還提供了 AllocObject 方法來建立對象,以一樣調用 Animal 類構造方法爲例:
/** * 經過 AllocObject 方法來建立一個類 */ extern "C" JNIEXPORT jobject JNICALL Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_allocObjectConstructor(JNIEnv *env, jobject instance) { jclass animalClass; jobject result; jmethodID mid; // 得到對應的 類 animalClass = env->FindClass("com/glumes/cppso/model/Animal"); if (animalClass == NULL) { return NULL; } // 得到構造方法 id mid = env->GetMethodID(animalClass, "<init>", "(Ljava/lang/String;)V"); if (mid == NULL) { return NULL; } // 構造方法的參數 jstring args = env->NewStringUTF("use AllocObject"); // 建立對象,此時建立的對象未初始化的對象 result = env->AllocObject(animalClass); if (result == NULL) { return NULL; } // 調用 CallNonvirtualVoidMethod 方法去調用類的構造方法 env->CallNonvirtualVoidMethod(result, animalClass, mid, args); if (env->ExceptionCheck()) { env->DeleteLocalRef(result); return NULL; } return result; }
一樣的,要先準備必要的東西。得到對應類的類型、方法 id、構造方法的參數。
而後經過 AllocObject
方法建立對象,但要注意的是,此時建立的對象是未被初始化的,不一樣於 NewObject
方法建立的對象直接就是初始化了,在必定程度上,能夠說 AllocObject
方法是延遲初始化的。
接下來是要經過 CallNonvirtualVoidMethod
來調用對應的構造方法。此處傳入的一個參數再也不是 jclass 類型,而是建立的未被初始化的類 jobject 。
經過這種方法,一樣能夠建立一個 Java 中的類。
能夠經過 JNI 來調用父類的實例方法。
在子類中經過調用 CallNonvirtual<Type>Method
方法來調用父類的方法。
首先,構造一個相應的子類,而後得到父類的 類型和方法 id,以及準備對應的參數,根據父類方法的返回值選擇調用不一樣的 CallNonvirtual<Type>Method
函數。
對於引用類型的,調用 CallNonvirtualObjectMethod 方法;對於基礎類型的,調用 CallNonvirtualBooleanMethod、CallNonvirtualIntMethod 等等;對於無返回值類型的,調用 CallNonvirtualVoidMethod 方法。
具體看代碼:
/** * 調用父類的方法 * 建立一個子類,由子類去調用父類的方法 */ extern "C" JNIEXPORT void JNICALL Java_com_glumes_cppso_jnioperations_InvokeConstructorOps_callSuperMethod(JNIEnv *env, jobject instance) { jclass cat_cls; // Cat 類的類型 jmethodID cat_cid; // Cat 類的構造方法 id jstring cat_name; // Cat 類的構造方法參數 jobject cat; // 得到對應的 類 cat_cls = env->FindClass("com/glumes/cppso/model/Cat"); if (cat_cls == NULL) { return; } // 得到構造方法 id cat_cid = env->GetMethodID(cat_cls, "<init>", "(Ljava/lang/String;)V"); if (cat_cid == NULL) { return; } // 準備構造方法的參數 cat_name = env->NewStringUTF("this is cat name"); // 建立 Cat 類 cat = env->NewObject(cat_cls, cat_cid, cat_name); if (cat == NULL) { return; } //調用父類的 getName 參數 jclass animal_cls; // 父類的類型 jmethodID animal_mid; // 被調用的父類的方法 id // 得到父類對應的類 animal_cls = env->FindClass("com/glumes/cppso/model/Animal"); if (animal_cls == NULL) { return; } // 得到父類被調用的方法 id animal_mid = env->GetMethodID(animal_cls, "getName", "()Ljava/lang/String;"); if (animal_mid == NULL) { return; } jstring name = (jstring) env->CallNonvirtualObjectMethod(cat, animal_cls, animal_mid); if (name == NULL) { return; } LOGD("getName method value is %s", env->GetStringUTFChars(name, NULL)); // 調用父類的其餘方法 animal_mid = env->GetMethodID(animal_cls, "callInstanceMethod", "(I)V"); if (animal_mid == NULL) { return; } env->CallNonvirtualVoidMethod(cat, animal_cls, animal_mid); }
Cat 類做爲 Animal 類的子類,首先由 NewObject 方法建立 Cat 類,而後調用它的父類的方法。
由此,經過 JNI 來調用 Java 算是基本完成了。
具體示例代碼可參考個人 Github 項目,歡迎 Star。
https://github.com/glumes/AndroidDevWithCpp
歡迎關注微信公衆號:【紙上淺談】,得到最新文章推送~~