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

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

3 . 代码示例 : 调用 Student 类的 logInfo 方法 ;


student 是 Student 类对象 ,
“method_logInfo” 是方法 ID
info : jstring 类型参数 , 传入字符串到 Java 层运行 ( 注意 : 参数 必须 都是 Java 类型 ) ;
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 . 完整代码示例


完整代码示例 :


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



目录
相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
16 2
|
7天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
52 6
|
14天前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
26天前
|
设计模式 算法 安全
实时操作系统(RTOS)深度解析及Java实现初探
【10月更文挑战第22天】实时操作系统(RTOS,Real-Time Operating System)是一种能够在严格的时间限制内响应外部事件并处理任务的操作系统。它以其高效、高速、可靠的特点,广泛应用于工业自动化、航空航天、医疗设备、交通控制等领域。本文将深入浅出地介绍RTOS的相关概念、底层原理、作用与功能,并探讨在Java中实现实时系统的方法。
58 1
|
19天前
|
安全 Java 测试技术
🎉Java零基础:全面解析枚举的强大功能
【10月更文挑战第19天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
101 60
|
5天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
18 4
|
8天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
12天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
11天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
32 2
|
11天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。

推荐镜像

更多