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 方法传入的 )
struct _JNIEnv { /* _JNIEnv 结构体中封装了 JNINativeInterface 结构体指针 */ const struct JNINativeInterface* functions; ... // 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetObjectClass方法 jclass GetObjectClass(jobject obj) { return functions->GetObjectClass(this, obj); } ... }
2 . 代码示例 :
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” ;
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 对象 ;
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:\002_WorkSpace\001_AS\001_NDK_Hello\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes , 在这个目录下执行 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 命令 ;
Y:\002_WorkSpace\001_AS\001_NDK_Hello\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes>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 ; 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"); ... }