【Android NDK 开发】JNI 方法解析 ( C/C++ 调用 Java 方法 | 函数签名 | 调用对象方法 | 调用静态方法 )

2023-03-27 17:53:19 浏览数 (1)

文章目录
  • 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_WorkSpace01_AS01_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_WorkSpace01_AS01_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 命令获得 ;
代码语言:javascript复制
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 命令获得 ;
代码语言:javascript复制
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 类型 ) ;
代码语言:javascript复制
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 类型 ) ;
代码语言:javascript复制
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);


}

0 人点赞