1. 程式人生 > >[JNI]開發之旅(7)JNI函式中呼叫java物件的方法

[JNI]開發之旅(7)JNI函式中呼叫java物件的方法

在jni函式中我們不僅要對java物件的資料域進行訪問,而且有時也需要呼叫java中類物件已經實現的方法。接下來我們對物件的方法呼叫,呼叫步驟與訪問資料域相似。

1.獲得例項對應的class2.根據class類獲得方法的method id
3.根據method id和物件例項獲取方法
4.操作方法

例項1:JNI方法java物件public方法

java程式碼
定義一個setSex方法,通過accessPublicMethod在jni實現給java物件的變數sex賦值

    private String sex = "female";//需要賦初始值或定義成static,不然在沒有呼叫accessPublicMethod方法前,呼叫getSex方法會拋異常
public void setSex(String sex){ this.sex = sex; } public String getSex(){ return sex; } public native void accessPublicMethod();

JNI層程式碼


//訪問java中public方法
extern "C"
void Java_com_honjane_ndkdemo_JNIUtils_accessPublicMethod( JNIEnv* env, jobject jobj){
    //1.獲得例項對應的class類
jclass jcls = env->GetObjectClass(jobj); //2.通過class類找到對應的method id //name 為java類中變數名,Ljava/lang/String; 為變數的型別String jmethodID jmid = env->GetMethodID(jcls,"setSex","(Ljava/lang/String;)V"); //定義一個性別賦值給java中的方法 char c[10] = "male"; jstring jsex = env->NewStringUTF(c); //3.通過obj獲得對應的method
env->CallVoidMethod(jobj,jmid,jsex); }

這裡有幾個地方注意GetMethodID獲取methodID需要傳入jclass,CallVoidMethod需要傳入物件例項jobject,前面文章以及介紹過原因,這裡不在多說;Ljava/lang/String不要寫成Ljava.lang.String否則會找不到方法。

執行結果:

I/main----sex賦值前: female
I/main----sex賦值後: male

呼叫private方法就不用例項實現了,只需要把setSex方法改成private,其他程式碼實現與public沒有區別。在c/c++層,java的訪問域無效。

例項2:JNI方法java物件static方法

java層程式碼:定義一個static的getHeight()方法,通過accessStaticMethod在jni函式中實現呼叫。


    private static int height = 160;

    public static int getHeight(){
        return height;
    }

    public native int accessStaticMethod();

jni實現:


//訪問java中static方法
extern "C"
jint Java_com_honjane_ndkdemo_JNIUtils_accessStaticMethod( JNIEnv* env, jobject jobj){
    //1.獲得例項對應的class類
    jclass jcls = env->GetObjectClass(jobj);

    //2.通過class類找到對應的method id
    jmethodID jmid = env->GetStaticMethodID(jcls,"getHeight","()I");

    //3.靜態方法通過class獲得對應的method
    return env->CallStaticIntMethod(jcls,jmid);
}

執行結果:

I/main----height呼叫JNI方法: 160

例項3:JNI函式訪問java類的父類方法

java層程式碼:
定義一個SuperUtils讓前面定義的JNIUtils繼承於它,然後在SuperUtils中定義一個hello方法,通過jni實現呼叫java父類方法


public class SuperUtils {

    public String hello(String msg){
        return "2017 " + msg;
    }
}

    .....

 public class JNIUtils extends SuperUtils{

    public native String accessSuperMethod();

    ......
 }

JNI程式碼實現:




//訪問java中父類方法
extern "C"
jstring Java_com_honjane_ndkdemo_JNIUtils_accessSuperMethod( JNIEnv* env, jobject jobj){
    //1.通過反射獲得父類的class類
    jclass jpcls = env->FindClass("com/honjane/ndkdemo/SuperUtils");
    if(jpcls == NULL){
        char c[10] = "error";
        return env->NewStringUTF(c);
    }
    //2.通過class類找到對應的method id
    jmethodID jmid = env->GetMethodID(jpcls,"hello","(Ljava/lang/String;)Ljava/lang/String;");
    char c[20] = "happy new year";
    jstring new_str = env->NewStringUTF(c);
    //3.靜態方法通過class獲得對應的method
    return (jstring)env->CallNonvirtualObjectMethod(jobj,jpcls,jmid,new_str);
}

這裡與前面有幾個不同點:
1.通過反射FindClass獲取到父類的class,而不是JNIUtils的class
2.呼叫CallNonvirtual【Type】Method方法來實現呼叫父類方法

Nonvirtual:繼承而來的非虛擬函

這節簡單的介紹了jni函式怎麼呼叫java物件的方法及父類的方法,要熟悉可以多動手按照上面介紹的步驟,去實現其他型別返回值/引數型別的方法,加深印象。下一節將介紹對java物件構造器的訪問。