先看一段Java代碼,java
Java package org.professor.jni.animal; import android.util.Log; public class Animal { protected String name; public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void eat() { Log.i("ANIMAL", "ANIMAL EAT FOOD"); } } package org.professor.jni.animal; import android.util.Log; public class Bird extends Animal { public Bird(String name) { super(name); Log.i("ANIMAL", "BIRD Constructor"); } @Override public String getName() { Log.i("ANIMAL", "BIRD Get Name Method"); return name; } @Override public void eat() { Log.i("ANIMAL", "BIRD EAT MI"); } }
簡單看下上面代碼,很簡單的兩個類,父類 Animal
,子類 Bird
, Animal
類中定義了 eat()
和 getName()
兩個方法,Bird
繼承自 Animal
,並重寫了父類的兩個方法。若是調用的話能夠用 Animal bird = new Bird("Professor");
, bird.eat()
。在執行 new Bird("Professor")
這段代碼時, 會先爲 Bird 類分配內存空間(所分配的內存空間大小由 Bird 類的成員變量數量決定),而後調用 Bird 的帶參構造方法初始化對象。 Bird 是 Animal 類型,但它指向的是 Bird 實例對象的引用,並且 Bird 重寫了父類的 eat 方法,由於調用 eat 方法時有多態存在,因此訪問的是 Bird 的 eat 而非 Animal 的 eat,運行後打印的結果爲: BIRD EAT MI
; 若是要調用父類的 eat()
方法,只需在 Cat 的 eat()
方法中調用 super.eat()
便可。android
在C / C++ 中,棧空間和堆空間,棧空間的內存大小受操做系統限制,由操做系統自動來管理,速度較快,因此在函數中定義的局部變量、函數形參變量都存儲在棧空間。操做系統沒有限制堆空間的內存大小,只受物理內存的限制,內存須要程序員本身管理。在 C 語言中用 malloc
關鍵字動態分配的內存和在 C++ 中用 new 建立的對象所分配內存都存儲在堆空間,內存使用完以後分別用 free
或 delete/delete[]
釋放。程序員
頭文件ide
// // Created by Peng Cai on 2018/10/16. // # include # ifndef ANDROIDJNIDEMO_CALL_SUPER_H # define ANDROIDJNIDEMO_CALL_SUPER_H # ifdef __cplusplus extern "C" { # endif /* - Class: org_professor_jni_MainActivity - Method: callNativeSuperInstanceMethod - Signature: ()V */ JNIEXPORT void JNICALL Java_org_professor_jni_MainActivity_callNativeSuperInstanceMethod (JNIEnv *, jobject); # ifdef __cplusplus } # endif # endif //ANDROIDJNIDEMO_CALL_SUPER_H
實現文件函數
// // Created by Peng Cai on 2018/10/16. // # include "include/call_super.h" # include # include # include JNIEXPORT void JNICALL Java_org_professor_jni_MainActivity_callNativeSuperInstanceMethod(JNIEnv *env, jobject instance) { //1. 獲取類類型 jclass birdClazz = (*env)->FindClass(env, "org/professor/jni/animal/Bird"); if (NULL == birdClazz) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Bird Class Failed"); return; } //2.獲取構造函數ID 構造方法的名統一爲:<init> jmethodID birdInstanceMethodId = (*env)->GetMethodID(env, birdClazz, "<init>", "(Ljava/long/String;)V"); if (NULL == birdInstanceMethodId) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Bird Instance Method Id Failed"); return; } //3. 建立一個對象實例 jstring name = (*env)->NewStringUTF(env, "YIMI"); if (NULL == name) { //有可能內存不夠 __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Bird Name Failed"); return; } jobject birdObj = (*env)->NewObject(env, birdClazz, birdInstanceMethodId, name); if (NULL == birdObj) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Bird Instance Obj Failed"); return; } // 4.獲取調用方法ID jmethodID birdGetNameMethodID = (*env)->GetMethodID(env, birdClazz, "getName", "()Ljava/long/String;"); if (NULL == birdGetNameMethodID) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Bird Get Name Method Id Failed"); return; } //5. 調用本身getName方法 jstring birdName = (*env)->CallObjectMethod(env, birdObj, birdGetNameMethodID); if (NULL != birdName) { const char *c_bird_name = (*env)->GetStringUTFChars(env, birdName, JNI_FALSE); //轉換編碼格式 在C裏面用 JNI_FALSE表明不拷貝到緩衝區 if (NULL != c_bird_name) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL bird name = %s", c_bird_name); //釋放從java層獲取到的字符串所分配的內存 (*env)->ReleaseStringUTFChars(env, birdName, c_bird_name); } } //-------華麗分割線-------------- //6. 獲取父類的Clazz jclass animalClazz = (*env)->FindClass(env, "org/professor/jni/Animal"); if (NULL == animalClazz) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Animal Class Failed"); return; } //7.獲取父類方法ID eat方法 jmethodID animalEatMethodID = (*env)->GetMethodID(env, animalClazz, "eat", "()V"); if (NULL == animalEatMethodID) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Animal Eat Method Id Failed"); return; } //8.子類調用父類方法 eat //注意:birdObj是Bird的實例,animalClazz是Animal的Class引用,animalEatMethodID是Animal類中的方法ID (*env)->CallNonvirtualVoidMethod(env, birdObj, animalClazz, animalEatMethodID); //--------------華麗分割線------------------ // 9.獲取父類調用方法getName jmethodID animalGetNameMethodID = (*env)->GetMethodID(env, animalClazz, "getName", "()Ljava/long/String;"); if (NULL == animalGetNameMethodID) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Animal Get Name Method Id Failed"); return; } //10.子類調用父類GetName 方法 jstring animalName = (*env)->CallNonvirtualObjectMethod(env, birdObj, animalClazz, animalGetNameMethodID); if (NULL != animalName) { const char *c_animal_name = (*env)->GetStringUTFChars(env, animalName, JNI_FALSE); if (NULL != c_animal_name) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL animal name = %s", c_animal_name); //釋放從java層獲取到的字符串所分配的內存 (*env)->ReleaseStringUTFChars(env, animalName, c_animal_name); } } quit: // 刪除局部引用變量 jmethod 不是引用類型 (jobject或jobject的子類才屬於引用變量) (*env)->DeleteLocalRef(env, birdClazz); (*env)->DeleteLocalRef(env, animalClazz); (*env)->DeleteLocalRef(env, name); (*env)->DeleteLocalRef(env, birdObj); (*env)->DeleteLocalRef(env, birdName); (*env)->DeleteLocalRef(env, animalName); } ```
調用構造方法ui
jobject birdObj = (*env)->NewObject(env, birdClazz, birdInstanceMethodId, name);
咱們經過上面的代碼去建立一個爲初始化的對像並分配非存空間,而後調用對象的構造器去初始化對象。其實也能夠經過下民的方式去作this
jobject birdObj = (*env)->AllocObject(env, birdClazz); if(NULL !=birdObj){ (*env)->CallNonvirtualVoidMethod(env,birdObj,birdClazz,birdInstanceMethodId,name); if((*env)->ExceptionCheck(env)){ goto quit; //跳轉到釋放函數表 } }
AllocObject
函數建立的是一個未初始化的對象,後面在用這個對象以前,必須調用CallNonvirtualVoidMethod
, 調用對象的構造函數初始化該對象。並且在使用時必定要很是當心,確保在一個對象上面,構造函數最多被調用一次。有時,先建立一個初始化的對象,而後在合適的時間再調用構造函數的方式是頗有用的。儘管如此,大部分狀況下,應該使用 NewObject,儘可能避免使用容易出錯的 AllocObject/CallNonvirtualVoidMethod 函數。編碼
調用實例方法操作系統
若是一個方法被定義在父類中,在子類中被覆蓋,也能夠調用父類中的這個實例方法。JNI 提供了一系列函數CallNonvirtualXXXMethod
來支持調用各類返回值類型的實例方法。調用一個定義在父類中的實例方法,須遵循下面的步驟:code
//7.獲取父類方法ID eat方法 jmethodID animalEatMethodID = (*env)->GetMethodID(env, animalClazz, "eat", "()V"); if (NULL == animalEatMethodID) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL", "Create Animal Eat Method Id Failed"); return; }
//8.子類調用父類方法 eat //注意:birdObj是Bird的實例,animalClazz是Animal的Class引用,animalEatMethodID是Animal類中的方法ID (*env)->CallNonvirtualVoidMethod(env, birdObj, animalClazz, animalEatMethodID);