【Android JNI】在C/C++中調用Java

JNI名詞解釋

jclass對應Java類的名字,jobject對應Java實例化後的對象,jfieldID對應Java類成員變量的ID,jmethodID對應Java類方法的ID。
JNI中對Java的調用,通常要先獲取jclass,然後生成實例化對象jobject,根據jclass可以獲取jfield&jmethodID,有了這幾個元素,可以對Java類的成員變量和方法進行操作。

生成Java對象 jobject

根據Java類名字jclass可以生成Java類對象,所以要先獲取Java類名字jclass。

獲取Java類名 jclass

如果知道Java類全名,可以使用FindClass函數獲取jclass,參數爲字符串類型(包名+類名)。或者如果已知實例化後的jobject,可以使用GetObjectClass函數獲取jclass,參數爲jobject實例。

jclass FindClass(const char* name);
jclass GetObjectClass(jobject obj);

獲取自定義類jclass

比如有包名爲package com.lmshao.jniexample;類名爲public class Student{},則獲取這個Student類名的方法爲:

jclass clazz = env->FindClass("com/lmshao/jniexample/Student");

示例:獲取內置類jclass

獲取Java內置類名方法爲:

jclass uuidClass = env->FindClass("java/util/UUID");        // UUID
jclass clazzBoolean = env->FindClass("java.util.HashMap");  // HashMap

示例:通過jobject獲取類jclass

如果已知jobject,可以根據jobject來獲取jclass,jclass會在接下來獲取類成員變量和方法時候用到。

void jniFunction(JNIEnv *env, jobject object) {
    jclass clazz = env->GetObjectClass(object);
    ....
}

生成Java對象 jobject

根據jclass實例化有兩種方式,一種是使用AllocObject隱式調用默認的構造函數實例化,另一種是使用NewObject和構造函數的jmethodID,顯式調用指定簽名的構造函數。
NewObject函數第二個參數jmethodID爲構造函數的ID,可以使用GetMethodID函數獲取這個變量。(GetMethodID函數第二個參數爲成員函數的名字,當獲取構造函數ID的時候,這個參數指定爲<init>,第三個參數爲方法簽名,詳細參考下面的調用Java類方法段落。)

jobject AllocObject(jclass clazz);
jobject NewObject(jclass, jmethodID, ...);

示例:AllocObject用法

假設有個Student類,接下來要調用默認構造函數進行實例化。

jclass studentClass = env->FindClass("com/lmshao/jniexample/Student");
jobject studentObject = env->AllocObject(studentClass);

示例:NewObject用法

假設Student類有個構造函數爲public Student(int mAge),接下來調用這個構造函數進行實例化。

jclass studentClass = env->FindClass("com/lmshao/jniexample/Student");
jmethodID constructorMethod = env->GetMethodID(studentClass, "<init>", "(I)V"); // param2: "<init>", param3:構造函數的簽名
int age = 26;
jobject studentObject = env->NewObject(studentClass, constructorMethod, (jint)age);

當然這種方法也可以調用沒有參數的默認構造函數。

jclass studentClass = env->FindClass("com/lmshao/jniexample/Student");
jmethodID constructorMethod = env->GetMethodID(studentClass, "<init>", "()V");
jobject studentObject = env->NewObject(studentClass, constructorMethod);

Java類成員變量操作 (Set & Get)

獲取Java類成員變量ID jfieldID

要對Java類成員變量進行操作,必須要先知道成員變量的ID。根據jclass可以使用GetFieldIDGetStaticFieldID函數獲取成員變量ID(jfieldID),前者表示非靜態變量後者表示靜態成員變量,用法一樣。參數1爲jclass,參數2爲成員變量名,參數3爲成員變量的簽名。關於Java基本數據類型的JNI簽名可以查看上一篇博客 的簽名部分。

jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);   // 非靜態成員變量
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig);   // 靜態成員變量

示例:獲取成員變量jfieldID方法

例如Student類中有String mName; int mAge;,則獲取成員變量jfieldID方法爲:

jclass studentClass = env->FindClass("com/lmshao/jniexample/Student");

jfieldID nameField  = env->GetFieldID(studentClass, "mName", "Ljava/lang/String;");
jfieldID ageField   = env->GetFieldID(studentClass, "mAge", "I");

Set非靜態成員變量

對成員變量操作的函數都與變量的類型相關,有一系列針對不同變量類型(XXX)的函數SetXXXField可以使用,用法類似,使用時根據變量類型進行選擇。

void SetObjectField(jobject obj, jfieldID fieldID, jobject value);
void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value);
void SetByteField(jobject obj, jfieldID fieldID, jbyte value);
void SetCharField(jobject obj, jfieldID fieldID, jchar value);
void SetShortField(jobject obj, jfieldID fieldID, jshort value);
void SetIntField(jobject obj, jfieldID fieldID, jint value);
void SetLongField(jobject obj, jfieldID fieldID, jlong value);
void SetFloatField(jobject obj, jfieldID fieldID, jfloat value);
void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value);

示例:非靜態成員變量賦值

假設CourseScore類中有成員變量mMath,接下來對這個變量進行賦值操作。

jclass courseScoreClass = env->FindClass("com/lmshao/jniexample/CourseScore");  // get jclass
jfieldID mathField = env->GetFieldID(courseScoreClass, "mMath", "I");   // get jfield
jobject courseScoreObject = env->AllocObject(courseScoreClass);  // get jobject
env->SetIntField(courseScoreObject, mathField, (jint)96);  // set field

Get非靜態成員變量

同樣也是一系列針對不同變量類型(XXX)的函數GetXXXField,用法類似,使用時根據變量類型進行選擇。

jobject GetObjectField(jobject obj, jfieldID fieldID);
jboolean GetBooleanField(jobject obj, jfieldID fieldID);
jbyte GetByteField(jobject obj, jfieldID fieldID);
jchar GetCharField(jobject obj, jfieldID fieldID);
jshort GetShortField(jobject obj, jfieldID fieldID);
jint GetIntField(jobject obj, jfieldID fieldID);
jlong GetLongField(jobject obj, jfieldID fieldID);
jfloat GetFloatField(jobject obj, jfieldID fieldID);
jdouble GetDoubleField(jobject obj, jfieldID fieldID);

示例:獲取非靜態成員變量值

假設CourseScore類中有成員變量mMath,在已經得到jobject的情況下獲取其中mMath變量的值。

// 已知 jobject courseScoreObject
jclass courseScoreClass = env->FindClass("com/lmshao/jniexample/CourseScore");  // get jclass
jfieldID mathField = env->GetFieldID(courseScoreClass, "mMath", "I");   // get jfield
jint score = env->GetIntField(courseScoreObject, mathField);  // get field

Set靜態成員變量

同樣也是一系列針對不同變量類型(XXX)的函數SetStaticXXXField,用法類似,使用時根據變量類型進行選擇。注意:第一個參數爲jclass,因爲靜態成員只屬於類class,而不屬於實例object。

void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value);
void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value);
void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value);
void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value);
void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value);
void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value);
void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value);
void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value);
void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value);

Get靜態成員變量

同樣也是一系列針對不同變量類型(XXX)的函數GetStaticXXXField,用法類似,使用時根據變量類型進行選擇。注意:第一個參數爲jclass,因爲靜態成員只屬於類class,而不屬於實例object。

jobject GetStaticObjectField(jclass clazz, jfieldID fieldID);
jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID);
jbyte GetStaticByteField(jclass clazz, jfieldID fieldID);
jchar GetStaticCharField(jclass clazz, jfieldID fieldID);
jshort GetStaticShortField(jclass clazz, jfieldID fieldID);
jint GetStaticIntField(jclass clazz, jfieldID fieldID);
jlong GetStaticLongField(jclass clazz, jfieldID fieldID);
jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID);
jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID);

調用Java類的方法(成員函數)

獲取Java類方法ID jmethodID

根據jclass可以使用GetMethodIDGetStaticMethodID函數獲取方法ID(jmethodID)。參數1爲jclass,參數2爲方法名,參數3爲方法的簽名。
方法和變量簽名可以使用命令javac Student.java編譯,然後使用命令javap -s -p Student.class查看簽名。

jmethodID GetMethodID(jclass clazz, const char* name, const char* sig); // 非靜態方法
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig);  //靜態方法

示例:獲取方法ID jmethodID

例如Student類中有void setName(String name); int getAge();方法,接下來獲取相應的jmethodID。

jclass studentClass = env->FindClass("com/lmshao/jniexample/Student");
jmethodID setnameMethod = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
jmethodID getageMethod = env->GetMethodID(studentClass, "getAge", "()I");

獲取Java類靜態方法ID與獲取非靜態方法ID的用法相同。

調用非靜態方法

同樣也是一系列針對不同變量類型(XXX)的函數GetXXXMethod,用法類似,使用時根據變量類型進行選擇。

jobject CallObjectMethod(jobject, jmethodID, ...);
jboolean CallBooleanMethod(jobject, jmethodID, ...);
jbyte CallByteMethod(jobject, jmethodID, ...);
jchar CallCharMethod(jobject, jmethodID, ...);
jshort CallShortMethod(jobject, jmethodID, ...);
jint CallIntMethod(jobject, jmethodID, ...);
jlong CallLongMethod(jobject, jmethodID, ...);
jfloat CallFloatMethod(jobject, jmethodID, ...);
jdouble CallDoubleMethod(jobject, jmethodID, ...);
void CallVoidMethod(jobject, jmethodID, ...);

示例:調用非靜態方法

例如Student類中有void setAge();方法,接下來調用這兩個方法。

jclass studentClass = env->FindClass("com/lmshao/jniexample/Student");
jmethodID setageMethod = env->GetMethodID(studentClass, "setAge", "(I)V");
jobject studentObject = env->AllocObject(studentClass);
env->CallVoidMethod(studentObject, setageMethod, (jint)26);  //沒有返回值

調用靜態方法

同樣也是一系列針對不同變量類型(XXX)的函數GetStaticXXXMethod,用法類似,使用時根據變量類型進行選擇。

jobject CallStaticObjectMethod(jclass, jmethodID, ...);
jboolean CallStaticBooleanMethod(jclass, jmethodID, ...);
jbyte CallStaticByteMethod(jclass, jmethodID, ...);
jchar CallStaticCharMethod(jclass, jmethodID, ...);
jshort CallStaticShortMethod(jclass, jmethodID, ...);
jint CallStaticIntMethod(jclass, jmethodID, ...);
jlong CallStaticLongMethod(jclass, jmethodID, ...);
jfloat CallStaticFloatMethod(jclass, jmethodID, ...);
jdouble CallStaticDoubleMethod(jclass, jmethodID, ...);
void CallStaticVoidMethod(jclass, jmethodID, ...);

示例:調用靜態方法

例如Student類中有static int getAge();方法,接下來調用這兩個方法。

jclass studentClass = env->FindClass("com/lmshao/jniexample/Student");
jmethodID getageMethod = env->GetStaticMethodID(studentClass, "getAge", "()I");
jint age = env->CallStaticIntMethod(studentClass, getageMethod);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章