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



目录
相关文章
|
1天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
12 4
|
2天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
7 0
|
3天前
|
Java API Android开发
kotlin和java开发优缺点
kotlin和java开发优缺点
12 0
|
28天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
63 0
|
28天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
50 0
|
28天前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
58 0
|
28天前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
74 0
|
9天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
28 3
|
26天前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
50 5
|
28天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)