JNI Access Parent Class Construction Method and Parent Class Instance Method
Constructing Method and Parent Class Instance Method
Let's start with a piece of Java code.
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"); } }
Looking at the above code, two simple classes, parent Animal, child Bird and Animal, define eat() and getName() methods. Bird inherits from Animal and rewrites the two methods of parent class. If called, you can use Animal bird = new Bird("Professor"); bird. eat(). When executing the new Bird("Professor") code, memory space is allocated to the Bird class (the size of the allocated memory space is determined by the number of member variables of the Bird class), and then Bird's parametric construction method is invoked to initialize the object. Bird is an Animal type, but it points to a reference to the Bird instance object, and Bird rewrites the eat method of the parent class, because there are polymorphisms when the eat method is called, so it accesses Bird's eat rather than Animal's eat, and the result printed after running is BIRD EAT MI; if you want to call the eat() method of the parent class, you only need to call super.e in Cat's eat() method. At () will do.
In C/C++, stack space and stack space, the memory size of stack space is limited by the operating system, which is automatically managed by the operating system, so the local variables and function parameters defined in the function are stored in the stack space. The operating system does not limit the memory size of heap space, only physical memory, memory needs to be managed by programmers themselves. The memory allocated dynamically with malloc keyword in C language and the memory allocated by objects created with new in C++ are stored in heap space. After the memory is used up, it is free ly or delete/delete [] released separately.
Native Layered Access Parent Construction Method and Parent Instance Method
-
header file
// // 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
-
Implementation file
// // 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. Getting class types 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. The name of the constructor ID constructor is unified as <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. Create an object instance jstring name = (*env)->NewStringUTF(env, "YIMI"); if (NULL == name) { //There may be insufficient memory __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. Get the calling method 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. Call your own getName method jstring birdName = (*env)->CallObjectMethod(env, birdObj, birdGetNameMethodID); if (NULL != birdName) { const char *c_bird_name = (*env)->GetStringUTFChars(env, birdName, JNI_FALSE); //Convert encoding format in C with JNI_FALSE to represent non-copy to buffer if (NULL != c_bird_name) { __android_log_print(ANDROID_LOG_WARN, "ANIMAL bird name = %s", c_bird_name); //Release memory allocated by strings retrieved from the java layer (*env)->ReleaseStringUTFChars(env, birdName, c_bird_name); } } //------------------------------------------------------------------------------------------------------------------------------ //6. Get Clazz of the parent class 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. Get the parent method ID eat method 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. Subclasses call the parent method eat //Note: birdObj is an example of Bird, animalClazz is a Classes reference to Animal, and animalEatMethodID is the method ID in the Animal Class. (*env)->CallNonvirtualVoidMethod(env, birdObj, animalClazz, animalEatMethodID); //---------------------------------------------------------------------------------------------------------------------------------------- // 9. Get the parent class call method 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. Subclasses call the parent GetName method 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); //Release memory allocated by strings retrieved from the java layer (*env)->ReleaseStringUTFChars(env, animalName, c_animal_name); } } quit: // Deleting local reference variable jmethod is not a reference type (a subclass of job or job object is a reference variable only) (*env)->DeleteLocalRef(env, birdClazz); (*env)->DeleteLocalRef(env, animalClazz); (*env)->DeleteLocalRef(env, name); (*env)->DeleteLocalRef(env, birdObj); (*env)->DeleteLocalRef(env, birdName); (*env)->DeleteLocalRef(env, animalName); } ```
summary
-
Call the constructor
jobject birdObj = (*env)->NewObject(env, birdClazz, birdInstanceMethodId, name);
We use the above code to create an object for initialization and allocate non-memory space, then call the constructor of the object to initialize the object. In fact, it can also be done through the way of subordinate people.
jobject birdObj = (*env)->AllocObject(env, birdClazz); if(NULL !=birdObj){ (*env)->CallNonvirtualVoidMethod(env,birdObj,birdClazz,birdInstanceMethodId,name); if((*env)->ExceptionCheck(env)){ goto quit; //Jump to the release function table } }
The AllocObject function creates an uninitialized object, which must then be initialized by calling the CallNonvirtualVoidMethod and the object's constructor before using it. And be very careful when using it to ensure that the constructor is called at most once on an object. Sometimes it's useful to create an initialized object and then call the constructor at the right time. Nevertheless, in most cases, you should use NewObject to avoid using error-prone AllocObject/CallNonvirtualVoidMethod functions.
-
Call instance methods
If a method is defined in the parent class and overridden in the child class, the instance method in the parent class can also be invoked. JNI provides a series of functions called NonvirtualXXXMethod to support invoking instance methods of various return value types. Calling an instance method defined in the parent class follows the following steps:
- Use the GetMethodID function to get the method ID from a Class reference to the parent class.
//7. Get the parent method ID eat method 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; }
- Input subclass object, parent Class reference, parent method ID and parameters, and call one of a series of functions such as CallNonvirtualVoidMethod, CallNonvirtualBooleanMethod, CallNonvirtualIntMethod. CallNonvirtualVoidMethod can also be used to call the constructor of the parent class.
//8. Subclasses call the parent method eat //Note: birdObj is an example of Bird, animalClazz is a Classes reference to Animal, and animalEatMethodID is the method ID in the Animal Class. (*env)->CallNonvirtualVoidMethod(env, birdObj, animalClazz, animalEatMethodID);