文章目录- I . 调用 Java 方法流程
- II . 获取 jclass 对象 ( GetObjectClass )
- III . 获取 jclass 对象 ( FindClass )
- IV . JNI 函数签名规则
- V . javap 获取函数签名 ( 推荐 )
- VI . 反射获取对象方法 ( GetMethodID )
- VII . 反射获取类静态方法 ( GetStaticMethodID )
- VIII . 调用 Java 对象方法 ( CallXxxMethod )
- IX . 调用 Java 类静态方法 ( CallStaticXxxMethod )
- X . 完整代码示例
I . 调用 Java 方法流程
JNI 中调用 Java 方法流程 :
① 获取 jclass 类型变量 :
调用 jclass GetObjectClass(jobject obj) 或 jclass FindClass(const char* name) 方法 , 获取 jclass 类型变量 ;
② 通过反射获取方法 :
调用 jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) 获取 Java 对象方法 , 调用 jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) 获取 Java 类的静态方法 ;
③ 调用方法 :
void CallVoidMethod(jobject obj, jmethodID methodID, …) 调用 Java 对象方法 , void CallStaticVoidMethod(jclass clazz, jmethodID methodID, …) 调用 Java 的静态方法 ;
CalXxxMethod 方法 , 其中的 Xxx 是 Java 对象的 返回值 , 不同的返回值调用不同的方法 ;
II . 获取 jclass 对象 ( GetObjectClass )
1 . 函数原型 : 通过传入 Java 对象 ( jobject 类型变量 ) , 获取 Java 类对象 ( jclass 类型变量 )
返回值 : 返回 Java 字节码 Class 对象 , 对应 C/C 中的 jclass 对象 ; 参数 : 传入 Java 对象 ; ( 该对象一般是由 JNI 方法传入的 )
代码语言:javascript复制struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetObjectClass方法
jclass GetObjectClass(jobject obj)
{ return functions->GetObjectClass(this, obj); }
...
}
2 . 代码示例 :
代码语言:javascript复制extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
//获取 Java 对应的 Class 对象
jclass student_class = env->GetObjectClass(student);
...
}
III . 获取 jclass 对象 ( FindClass )
函数原型 : 通过传入完整的 包名.类名 获取 Java 类对应的 C/C 环境下的 jclass 类型变量 ;
返回值 : 返回 Java 字节码 Class 对象 , 对应 C/C 中的 jclass 对象 ; 参数 : 传入 完整的 包名/类名 , 注意包名中使用 “/” 代替 “.” , 如 “kim/hsl/jni/Teacher” ;
代码语言:javascript复制struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 FindClass 方法
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
...
}
2 . 代码示例 : 获取 kim.hsl.jni.Teacher 对应的 jclass 对象 ;
代码语言:javascript复制extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniObjectTest(JNIEnv *env, jobject instance, jobject student) {
...
// 5.2 获取 Teacher 类 ( 该变量需要释放 )
jclass class_teacher = env->FindClass("kim/hsl/jni/Teacher");
...
}
IV . JNI 函数签名规则
参考 : JNI 函数签名规则
V . javap 获取函数签名 ( 推荐 )
自己写函数签名容易出错 , 还麻烦 , 推荐使用 javap 工具 ;
1 . 字节码文件 : 首先要先编译出 Student 的 class 字节码文件 , javap 命令要直接作用于该字节码文件 ;
2 . Android Studio 中 Java 代码编译后的 class 字节码文件位置 : 不同版本的 AS 编译后的字节码位置不同 , 建议在各自的 Module 下的 build 目录中进行文件查找 , 找到 class 字节码所在目录 ;
3 . 我的 AS 中目录位置是 : Y: 02_WorkSpace 01_AS 01_NDK_HelloappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses , 在这个目录下执行 javap -s kim.hsl.jni.Student 命令 , 即可获取类中的函数签名 ;
4 . javap 命令格式 : javap -s 完整包名.类名 ;
如 : 要获取 kim.hsl.jni.Student 类中的函数签名 , 使用 javap -s kim.hsl.jni.Student 命令 ;
5 . 执行命令 : 在 class 目录下执行 javap -s kim.hsl.jni.Student 命令 ;
代码语言:javascript复制Y: 02_WorkSpace 01_AS 01_NDK_HelloappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses>javap -s kim.hsl.jni.Student
Compiled from "Student.java"
public class kim.hsl.jni.Student {
public kim.hsl.jni.Student();
descriptor: ()V
public kim.hsl.jni.Student(int, java.lang.String);
descriptor: (ILjava/lang/String;)V
public int getAge();
descriptor: ()I
public void setAge(int);
descriptor: (I)V
public java.lang.String getName();
descriptor: ()Ljava/lang/String;
public void setName(java.lang.String);
descriptor: (Ljava/lang/String;)V
}
VI . 反射获取对象方法 ( GetMethodID )
函数原型 : 通过 jclass 对象 , 方法名称 , 和 方法签名 , 获取 Java 类对象对应的方法 ID 即 jmethodID 类型变量 ;
返回值 : Java 类对象对应的方法 ID ( jmethodID 类型变量 )
参数 :
- jclass clazz : 要获取的 Java 对象方法对应的 Java 类对象 ;
- const char* name : 方法名称 ;
- const char* sig : 方法签名 , 使用 javap 命令获得 ;
struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetMethodID 方法
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetMethodID(this, clazz, name, sig); }
...
}
2 . 代码示例 : 获取 Student 类的 getAge 方法 ;
student_class 是 Student 类对应的 C/C 中的 jclass 类型变量 , “getAge” 是方法名称 , “()I” 是方法签名 , 左侧的括号是参数列表类型签名 , 括号右的 I 是返回值类型 int ;
代码语言:javascript复制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 int getAge() 方法
jmethodID method_getAge = env->GetMethodID(student_class, "getAge" , "()I");
...
}
VII . 反射获取类静态方法 ( GetStaticMethodID )
函数原型 : 通过 jclass 对象 , 方法名称 , 和 方法签名 , 获取 Java 类对象对应的方法 ID 即 jmethodID 类型变量 ;
返回值 : Java 类对象对应的方法 ID ( jmethodID 类型变量 )
参数 :
- jclass clazz : 要获取的 Java 对象方法对应的 Java 类对象 ;
- const char* name : 方法名称 ;
- const char* sig : 方法签名 , 使用 javap 命令获得 ;
struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetStaticMethodID 方法
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetStaticMethodID(this, clazz, name, sig); }
...
}
2 . 代码示例 : 获取 Student 类的 getAge 方法 ;
student_class 是 Student 类对应的 C/C 中的 jclass 类型变量 , “logInfo” 是方法名称 , “(Ljava/lang/String;)V” 是方法签名 , 左侧的括号是参数列表类型签名 , 括号右的 V 是返回值类型 void ;
代码语言:javascript复制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");
...
}
VIII . 调用 Java 对象方法 ( CallXxxMethod )
注意 : 返回值和参数必须 都是 Java 类型 ;
函数原型 : 通过 Java 对象 , Java 方法 ID , 及根据函数签名传入的 参数列表 ( 可变参数 ) , 反射调用该 Java 对象的方法 ;
返回值 : Void , 注意这里的返回值可以是 8 种 基本数据类型 , jboolean , jbyte , jshort 等类型 , 也可以是引用类型 jobject 类型 , 只有这 10 种返回类型 , 没有其它类型 ; ( 注意 : 返回值 都是 Java 类型 )
参数 :
- jobject obj : 要获取的 Java 对象方法对应的 Java 类对象 ;
- jmethodID methodID : 方法 ID ;
- … : 可变参数 , 方法的参数 ( 注意 : 参数 必须 都是 Java 类型 ) ;
struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 CallVoidMethod 方法
void CallVoidMethod(jobject obj, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallVoidMethodV(this, obj, methodID, args);
va_end(args);
}
...
}
2 . 所有 Java 方法调用返回值类型 :
代码语言:javascript复制 jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
3 . 代码示例 : 调用 Student 类的 getAge 方法 ;
student 是 Student 类对象 , “method_getAge” 是方法 ID 由于没有参数 , 因此后面的可变参数列表为空 ;
代码语言:javascript复制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 . 调用 Student 的 public int getAge() 方法
jint age = env->CallIntMethod(student, method_getAge);
...
}
IX . 调用 Java 类静态方法 ( CallStaticXxxMethod )
注意 : 返回值和参数必须 都是 Java 类型 ;
函数原型 : 通过 Java 类对象 ( Class 对象 对应 C/C jclass 类型对象 ) , Java 方法 ID , 及根据函数签名传入的 参数列表 ( 可变参数 ) , 反射调用该 Java 对象的方法 ;
返回值 : Void , 注意这里的返回值可以是 8 种 基本数据类型 , jboolean , jbyte , jshort 等类型 , 也可以是引用类型 jobject 类型 , 只有这 10 种返回类型 , 没有其它类型 ; ( 注意 : 返回值 都是 Java 类型 )
参数 :
- jobject obj : 要获取的 Java 对象方法对应的 Java 类对象 ;
- jmethodID methodID : 方法 ID ;
- … : 可变参数 , 方法的参数 ( 注意 : 参数 必须 都是 Java 类型 ) ;
struct _JNIEnv {
/* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */
const struct JNINativeInterface* functions;
...
// 最终 调用的 还是 JNINativeInterface 结构体中封装的 CallStaticVoidMethodV 方法
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallStaticVoidMethodV(this, clazz, methodID, args);
va_end(args);
}
...
}
2 . 所有 Java 方法调用返回值类型 :
代码语言:javascript复制 jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
va_list);
jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
3 . 代码示例 : 调用 Student 类的 logInfo 方法 ;
student 是 Student 类对象 , “method_logInfo” 是方法 ID info : jstring 类型参数 , 传入字符串到 Java 层运行 ( 注意 : 参数 必须 都是 Java 类型 ) ;
代码语言:javascript复制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 . 完整代码示例
完整代码示例 :
代码语言:javascript复制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);
}