【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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章