Android JNI Development Series (11) JNI Access Parent Class Construction Method and Parent Class Instance Method

Keywords: Mobile Java Android encoding C

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);
    

Posted by fallenangel1983 on Fri, 01 Feb 2019 10:15:15 -0800