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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【Android NDK 开发】JNI 方法解析 ( C/C++ 调用 Java 方法 | 函数签名 | 调用对象方法 | 调用静态方法 )(一)

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 命令 , 即可获取类中的函数签名 ;


image.png



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");
  ...
}


目录
相关文章
|
1月前
|
安全 编译器 程序员
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
46 2
|
19天前
|
自然语言处理 编译器 Linux
|
24天前
|
自然语言处理 编译器 Linux
告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐
本文中,阿里云智能集团开发工程师李泽政以 Alinux 为操作环境,讲解模块相比传统头文件有哪些优势,并通过若干个例子,学习如何组织一个 C++ 模块工程并使用模块封装第三方库或是改造现有的项目。
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
25 1
|
1月前
|
Java 编译器 C语言
【一步一步了解Java系列】:Java中的方法对标C语言中的函数
【一步一步了解Java系列】:Java中的方法对标C语言中的函数
22 3
|
1月前
|
存储 编译器 C语言
C++类与对象深度解析(一):从抽象到实践的全面入门指南
C++类与对象深度解析(一):从抽象到实践的全面入门指南
48 8
|
1月前
|
存储
atoi函数解析以及自定义类型经典练习题
atoi函数解析以及自定义类型经典练习题
38 0
|
1月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
深入探索:Python中的并发编程新纪元——协程与异步函数解析
27 3
|
1月前
|
机器学习/深度学习 算法 C语言
【Python】Math--数学函数(详细附解析~)
【Python】Math--数学函数(详细附解析~)
|
1月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
36 4

推荐镜像

更多