3 . 代码示例 : 调用 Student 类的 logInfo 方法 ;
student 是 Student 类对象 , “method_logInfo” 是方法 ID info : jstring 类型参数 , 传入字符串到 Java 层运行 ( 注意 : 参数 必须 都是 Java 类型 ) ; extern "C" JNIEXPORT void JNICALL Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) { ... //1 . 获取 Java 对应的 Class 对象 jclass student_class = env->GetObjectClass(student); //2 . 获取 Student 的 public static void logInfo(String info) 方法 // 注意这里要使用 GetStaticMethodID 方法反射该静态方法 jmethodID method_logInfo = env->GetStaticMethodID(student_class, "logInfo" , "(Ljava/lang/String;)V"); //3 . 准备传入的 jstring 参数 jstring info = env->NewStringUTF("C/C++ 创建的 Java 字符串"); //4 . 调用静态方法 : 注意传入的参数必须都是 Java 类型的参数 env->CallStaticVoidMethod(student_class, method_logInfo, info); ... }
X . 完整代码示例
完整代码示例 :
extern "C" JNIEXPORT void JNICALL Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) { /* 参数解析 : JNIEnv *env : JNI 环境 , 结构体指针 , 结构体中封装了 209 个方法 jobject instance : 是 MainActivity 对象 jobject student : Java 层创建的 Student 对象 , 传入 Native 层 */ //在 C/C++ 中调用 Student 对象的 get 方法 //1 . 获取 Java 对应的 Class 对象 jclass student_class = env->GetObjectClass(student); //2 . 通过 Class 的反射获取要调用的方法 /* 函数原型 : jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetMethodID(this, clazz, name, sig); } 参数说明 : jclass clazz : 使用 GetObjectClass 方法获取的返回值 const char* name : 要调用的方法名称 const char* sig : 函数签名 , 具体的签名规则查看签名表格 public int getAge() 函数签名 : ()I 左侧的 () : 左侧的括号是参数列表类型签名 右侧的 I : 括号右的 I 是返回值类型 int public void setAge(int age) 函数签名 : (I)V (I) 表示左侧的参数列表 右侧的 V 表示其返回值类型是 void 类型 引用类型签名 : L + 全限定名 + ; javap 工具 : 可以使用 javap 工具获取方法签名 */ //获取 Student 的 public int getAge() 方法 jmethodID method_getAge = env->GetMethodID(student_class, "getAge" , "()I"); //获取 Student 的 public void setAge(int age) 方法 jmethodID method_setAge = env->GetMethodID(student_class, "setAge" , "(I)V"); //获取 Student 的 public static void logInfo(String info) 方法 // 注意这里要使用 GetStaticMethodID 方法反射该静态方法 jmethodID method_logInfo = env->GetStaticMethodID(student_class, "logInfo" , "(Ljava/lang/String;)V"); //3 . 调用 Java 对象的方法 /* 调用 Java 引用对象的方法 : 要根据 返回值类型不同 , 调用不同的方法 如果返回值是 int 类型 , 那么就需要调用 CallIntMethod 方法 如果返回值是 void 类型 , 那么就需要调用 CallVoidMethod 方法 如果调用的是静态方法 , 那么需要调用 ( 注意 : 调用方法时传入的参数都必须是 C/C++ 中的 Java 类型参数 , 如 jint , jstring 等 ) */ //调用 Student 对象的 public void setAge(int age) 方法 env->CallVoidMethod(student, method_setAge, 18); //调用 Student 的 public int getAge() 方法 jint age = env->CallIntMethod(student, method_getAge); /* 调用静态方法 : 1 . 创建 Java 字符串 2 . 调用静态方法 3 . 释放 Java 字符串 */ // 创建 Java 字符串 jstring info = env->NewStringUTF("C/C++ 创建的 Java 字符串"); // 调用静态方法 : 注意传入的参数 env->CallStaticVoidMethod(student_class, method_logInfo, info); // jstring info 在方法中创建新的字符串 , 需要在方法结束之前释放该引用对象 env->DeleteLocalRef(info); //4 . 设置 Student 对象属性 /* 反射获取属性 函数原型 : jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) { return functions->GetFieldID(this, clazz, name, sig); } 参数说明 : jclass clazz : Java 类对象 const char* name : 属性名称 const char* sig : 属性类型签名 设置反射属性值 : 函数原型 : void SetIntField(jobject obj, jfieldID fieldID, jint value) { functions->SetIntField(this, obj, fieldID, value); } 参数说明 : jobject obj : 设置对象 jfieldID fieldID : 通过 GetFieldID 方法获取的属性 ID jint value : 要设置的值 注意 : 设置不同类型的值 , 调用不同的设置方法 */ jfieldID age_field_id = env->GetFieldID(student_class, "age", "I"); env->SetIntField(student, age_field_id, 90); // 验证是否设置成功 age = env->CallIntMethod(student, method_getAge); //5 . 在 JNI 中创建 java 对象 , 并设置给另一个对象 /* 获取 Teacher 类 : 通过调用 FindClass 方法获取 Teacher 类 目前已知两种获取 jclass 的方法 获取 Teacher 类的构造方法 public Student(int age, String name) 构造方法的方法名都是 "<init>" 构造方法的函数签名为 此处还要特别注意 : 传入到 Java 方法中的参数 , 必须都是 Java 参数类型 如 jstring , jint , jintArray 等类型 , 不能将 C/C++ 类型传入参数 尤其是 char* 字符串 , 需要使用 NewStringUTF 将 C/C++ 字符串转为 jstring 类型字符串 创建 Teacher 对象 将 Teacher 对象设置给 Student 对象 */ // 5.1 获取 Student 的 public void setTeacher(Teacher teacher) 方法 // 注意这个 Teacher 的类型签名是 Lkim/hsl/jni/Teacher; jmethodID method_setTeacher = env->GetMethodID(student_class, "setTeacher" , "(Lkim/hsl/jni/Teacher;)V"); LOGE("method_setTeacher"); // 5.2 获取 Teacher 类 ( 该变量需要释放 ) jclass class_teacher = env->FindClass("kim/hsl/jni/Teacher"); // 5.3 查找构造方法 jmethodID method_init = env->GetMethodID(class_teacher, "<init>", "(ILjava/lang/String;)V"); // 5.4 准备 Java 类型参数 ( 该变量需要释放 ) // 此处特别注意 : 传入到 Java 方法中的参数都必须是 Java 参数 jint teacher_age = 88; jstring teacher_name = env->NewStringUTF("Tom Wang"); // 5.5 创建 Teacher 对象 ( 该变量需要释放 ) jobject teacher = env->NewObject(class_teacher, method_init, teacher_age, teacher_name); // 5.6 调用 Student 对象的 setTeacher 设置方法 env->CallVoidMethod(student, method_setTeacher, teacher); // 5.7 释放上面通过 FindClass NewStringUTF NewObject 创建的引用变量 , 便于节省内存 , 也可以等到 作用域结束 自动释放 // 使用完这三个引用之后 , 不再使用 ; 这里特别建议手动释放三个引用 // 如果不手动释放 , 在 该引用 作用域 结束后 , 也会自动释放掉 env->DeleteLocalRef(teacher_name); env->DeleteLocalRef(teacher); env->DeleteLocalRef(class_teacher); }