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

简介: 【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");
  ...
}


目录
相关文章
|
8月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
508 18
|
9月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
263 11
|
8月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
351 4
|
8月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
475 5
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
915 140
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
551 2
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1461 29
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
578 4
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。

推荐镜像

更多
  • DNS