先看一段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);