Android JNI開發系列(十一) JNI 訪問父類的構造方法和父類實例方法

JNI 訪問父類的構造方法和父類實例方法

構造方法和父類實例方法

先看一段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 建立的對象所分配內存都存儲在堆空間,內存使用完以後分別用 freedelete/delete[] 釋放。程序員

Native層發訪問父類構造方法和父類實例方法

  • 頭文件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

    • 使用 GetMethodID 函數從一個指向父類的 Class 引用當中獲取方法 ID。
    //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;
    }
    • 傳入子類對象、父類 Class 引用、父類方法 ID 和參數,並調用 CallNonvirtualVoidMethod、 CallNonvirtualBooleanMethod、CallNonvirtualIntMethod 等一系列函數中的一個。其中CallNonvirtualVoidMethod 也能夠被用來調用父類的構造函數。
    //8.子類調用父類方法 eat
    //注意:birdObj是Bird的實例,animalClazz是Animal的Class引用,animalEatMethodID是Animal類中的方法ID
    (*env)->CallNonvirtualVoidMethod(env, birdObj, animalClazz, animalEatMethodID);
相關文章
相關標籤/搜索