【Android NDK 开发】JNI 方法解析 ( 字符串数组参数传递 | 字符串遍历 | 类型强转 | Java 字符串与 C 字符串转换 | 字符串释放 )

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【Android NDK 开发】JNI 方法解析 ( 字符串数组参数传递 | 字符串遍历 | 类型强转 | Java 字符串与 C 字符串转换 | 字符串释放 )

文章目录

I . C/C++ 中的 Java 字符串数组类型

II . 获取字符串数组长度

III . 获取字符串数组元素

IV . 类型强转 ( jobject -> jstring )

V . 字符串转换 ( jstring -> char* )

VI . 字符串释放

VII . 部分代码示例



I . C/C++ 中的 Java 字符串数组类型


JNI 中 C/C++ 代码里的 Java 字符串数组类型 : jobjectArray ;


① JNI 类型现状 : 在 JNI 中没有定义 Java 字符串数组类型 , 只定义了 Java 字符串类型 jstring ;


② Object 对应 jobject : 在 C/C++ 环境中 jobject 类型对应 Java 中的 Object 类型 , Java 中字符串也是 Object 类型的 ;


③ 字符串数组类型 : 因此在 C/C++ 环境中使用 对象数组 jobjectArray 来当做 字符串数组类型 ;




II . 获取字符串数组长度


1 . 前提条件 : Java 层传入参数 jobjectArray stringArray , 该参数对应 Java 代码中的 String[] stringArray 参数 ;


下面的 GetArrayLength 方法的 stringArray 参数 , 就是 jobjectArray 类型的 ;



2 . 获取字符串数组长度 代码示例 : 其中返回值 jsize 是 int 类型的别名 ;


jsize stringArrayLength = env->GetArrayLength(stringArray);



GetArrayLength 方法详细解析参考如下内容 :

GetArrayLength 方法解析 ( 获取 jarray 数组长度 )




III . 获取字符串数组元素


C/C++ 代码中获取指定索引的 Java 字符串数组类型的元素 ;



1 . 调用方法 : 调用 JNIEnv * env 的 GetObjectArrayElement 方法 , 可以获取指定索引的 jobject 引用类型变量 ;



2 . 方法原型 :


struct _JNIEnv {
    /* _JNIEnv  结构体中封装了 JNINativeInterface 结构体指针 */
    const struct JNINativeInterface* functions;
    ...
    // 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetObjectArrayElement方法
    jobject GetObjectArrayElement(jobjectArray array, jsize index)
    { return functions->GetObjectArrayElement(this, array, index); }
    ...
}


3 . GetObjectArrayElement 参数说明 :


① jobjectArray array 参数 : 由 Java 层传入的 Java 对象数组 ;


② jsize index 参数 : 要获取的数组元素的索引值 ;



4 . 获取对象数组指定元素代码示例 :


其中的参数 stringArray 是 jobjectArray 类型的 , 由 JNI 方法传入 ;


其中的参数 i 是 int 类型的 , 是要获取的元素的索引值 , 从 0 开始计数 ;


jobject string_object = env->GetObjectArrayElement(stringArray, i);




IV . 类型强转 ( jobject -> jstring )


将 jobject 转为 jstring 类型 : string_object 是 jobject 类型变量 ;


 

// 2.2 将 jobject 类型强转成 jstring 类型 , 这两个都代表了 Java 的数据类型
        jstring string_java = static_cast<jstring>(string_object);



详细内容参考下面博客 :

【C++ 语言】类型转换 ( 转换操作符 | const_cast | static_cast | dynamic_cast | reinterpret_cast | 字符串转换 )




V . 字符串转换 ( jstring -> char* )


1 . GetStringUTFChars 方法 : 将 jstring 类型字符串 ( Java 中的字符串 ) 转为 char* 类型字符串 ( C/C++ 中的字符串 ) ;



2 . 函数原型 : jstring string 参数是 Java 通过 JNI 传入的 , 代表 Java 字符串 ;


struct _JNIEnv {
    /* _JNIEnv  结构体中封装了 JNINativeInterface 结构体指针 */
    const struct JNINativeInterface* functions;
    ...
    // 最终 调用的 还是 JNINativeInterface 结构体中封装的 GetStringUTFChars 方法
    const char* GetStringUTFChars(jstring string, jboolean* isCopy)
    { return functions->GetStringUTFChars(this, string, isCopy); }
    ...
}



3 . jboolean* isCopy 参数 : 该参数用于指定将 jintArray 类型的变量 , 转为 jint * 指针类型的变量 , 新的指针变量的生成方式 ;



① 将 该参数设置成指向 JNI_TRUE 的指针 : 将 int 数组数据拷贝到一个新的内存空间中 , 并将该内存空间首地址返回 ;


② 将 该参数设置成指向 JNI_FALSE 的指针 : 直接使用 java 中的 int 数组地址 , 返回 java 中的 int 数组的首地址 ;


③ 将 该参数设置成 NULL ( 推荐 ) : 表示不关心如何实现 , 让系统自动选择指针生成方式 , 一般情况下都不关心该生成方式 ;


④ jboolean 类型取值 : jboolean 的取值只能是 0 和 1 , 也可以使用 JNI_FALSE 和 JNI_TRUE 宏定义 ;


#define JNI_FALSE 0
#define JNI_TRUE 1


4 . jstring 字符串转换 为 char* 字符串示例 :


const char *string_c = env->GetStringUTFChars(string_java, JNI_FALSE);




VI . 字符串释放


1 . ReleaseStringUTFChars 方法 : 将 Java 字符串 和 C/C++ 字符串都释放 ;



2 . 函数原型 :


jstring string 参数是 Java 通过 JNI 传入的 , 代表 Java 字符串 ;
const char* utf 参数是通过 GetStringUTFChars 方法将上面的 Java 字符串转成的 C/C++ 字符串 ;
struct _JNIEnv {
    /* _JNIEnv  结构体中封装了 JNINativeInterface 结构体指针 */
    const struct JNINativeInterface* functions;
    ...
    // 最终 调用的 还是 JNINativeInterface 结构体中封装的 ReleaseStringUTFChars 方法
    void ReleaseStringUTFChars(jstring string, const char* utf)
    { functions->ReleaseStringUTFChars(this, string, utf); }
    ...
}




VII . 部分代码示例


部分代码示例 :


#include <jni.h>
#include <string>
//导入日志库
#include <android/log.h>
//定义日志宏 , 其中的 __VA_ARGS__ 表示可变参数
#define  LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"JNI",__VA_ARGS__);
...
extern "C"
JNIEXPORT void JNICALL
Java_kim_hsl_jni_MainActivity_jniArrayTest(JNIEnv *env, jobject instance, jintArray intArray_,
                                           jobjectArray stringArray) {
    ...
    // II . 引用类型数组操作 ( 获取字符串数组 )
    // 1 . 获取字符串数组长度
    jsize stringArrayLength = env->GetArrayLength(stringArray);
    // 2 . 遍历字符串数组
    for(int i = 0; i < stringArrayLength; i ++) {
        /*
            2.1 获取 jobject 数组中第 i 个元素
                注意 : 获取的是 jobject 类型变量
            函数原型 : jobject GetObjectArrayElement(jobjectArray array, jsize index)
                jobjectArray array 参数 : 是 Java 层传入的 Java 对象数组 参数 , 即 Native 层的调用函数的参数
                jsize index 参数 : 对象元素的索引值 , 取值范围 0 ~ stringArrayLength - 1
                返回值 : 返回的是 jobject 类型的变量
         */
        jobject string_object = env->GetObjectArrayElement(stringArray, i);
        // 2.2 将 jobject 类型强转成 jstring 类型 , 这两个都代表了 Java 的数据类型
        jstring string_java = static_cast<jstring>(string_object);
        /*
            2.3 将 jstring 类型转为 char* 类型
            jstring 类型简介 :
                class _jobject {};
                class _jstring : public _jobject {};
                typedef _jstring*       jstring;
                由上面可见 , jstring 只是 继承了 _jobject 类 , 没有任何实现 , 是一个空类
                因此需要借助 C/C++ 方法 将 java 类型的 jstring 字符串 其转为 C/C++ 类型的 char* 类型字符串
            转换函数原型 : void ReleaseStringUTFChars(jstring string, const char* utf)
         */
        const char *string_c = env->GetStringUTFChars(string_java, 0);
        // 2.4 打印 转换后的 字符串值
        __android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "打印字符串数组元素 : %d . %s", i, string_c);
        // 2.5 释放 char* 字符串
        env->ReleaseStringUTFChars(string_java, string_c);
    }
}



代码执行结果 :


01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印 int 数组元素 : 0 . 1
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印 int 数组元素 : 1 . 2
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印 int 数组元素 : 2 . 666
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印 int 数组元素 : 3 . 888
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印 int 数组元素 : 4 . 95555
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印字符串数组元素 : 0 . Hello
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印字符串数组元素 : 1 . World
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: 打印字符串数组元素 : 2 . Hanshuliang
01-12 19:45:59.634 11885-11885/kim.hsl.jni I/JNI_TAG: Java 层 jniArrayTest 执行完毕后 , int[] intArray 数组内容 : [8888, 8888, 8888, 8888, 8888]



限于篇幅就不再贴完整代码了 , 去下载博客资源有全套的 JNI 文档教程 , 及博客源码


目录
相关文章
|
2月前
|
Java 开发工具 Android开发
Android与iOS开发环境搭建全解析####
本文深入探讨了Android与iOS两大移动操作系统的开发环境搭建流程,旨在为初学者及有一定基础的开发者提供详尽指南。我们将从开发工具的选择、环境配置到第一个简单应用的创建,一步步引导读者步入移动应用开发的殿堂。无论你是Android Studio的新手还是Xcode的探索者,本文都将为你扫清开发道路上的障碍,助你快速上手并享受跨平台移动开发的乐趣。 ####
|
3月前
|
开发框架 供应链 监控
并行开发模型详解:类型、步骤及其应用解析
在现代研发环境中,企业需要在有限时间内推出高质量的产品,以满足客户不断变化的需求。传统的线性开发模式往往拖慢进度,导致资源浪费和延迟交付。并行开发模型通过允许多个开发阶段同时进行,极大提高了产品开发的效率和响应能力。本文将深入解析并行开发模型,涵盖其类型、步骤及如何通过辅助工具优化团队协作和管理工作流。
106 3
|
1月前
|
存储 Linux API
深入探索Android系统架构:从内核到应用层的全面解析
本文旨在为读者提供一份详尽的Android系统架构分析,从底层的Linux内核到顶层的应用程序框架。我们将探讨Android系统的模块化设计、各层之间的交互机制以及它们如何共同协作以支持丰富多样的应用生态。通过本篇文章,开发者和爱好者可以更深入理解Android平台的工作原理,从而优化开发流程和提升应用性能。
|
2月前
|
缓存 监控 网络协议
一文带你了解10大DNS攻击类型,收藏!
【10月更文挑战第23天】
488 1
一文带你了解10大DNS攻击类型,收藏!
|
2月前
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
2月前
|
Dart 安全 编译器
Flutter结合鸿蒙next 中数据类型转换的高级用法:dynamic 类型与其他类型的转换解析
在 Flutter 开发中,`dynamic` 类型提供了灵活性,但也带来了类型安全性问题。本文深入探讨 `dynamic` 类型及其与其他类型的转换,介绍如何使用 `as` 关键字、`is` 操作符和 `whereType&lt;T&gt;()` 方法进行类型转换,并提供最佳实践,包括避免过度使用 `dynamic`、使用 Null Safety 和异常处理,帮助开发者提高代码的可读性和可维护性。
108 1
|
3月前
|
存储 缓存 算法
Java 数组
【10月更文挑战第19天】Java 数组是一种非常实用的数据结构,它为我们提供了一种简单而有效的方式来存储和管理数据。通过合理地使用数组,我们能够提高程序的运行效率和代码的可读性。更加深入地了解和掌握 Java 数组的特性和应用,为我们的编程之旅增添更多的精彩。
42 4
|
2月前
|
安全 Java Linux
深入解析Android系统架构及其对开发者的意义####
【10月更文挑战第21天】 本文旨在为读者揭开Android操作系统架构的神秘面纱,探讨其如何塑造现代移动应用开发格局。通过剖析Linux内核、硬件抽象层、运行时环境及应用程序框架等关键组件,揭示Android平台的强大功能与灵活性。文章强调了理解Android架构对于开发者优化应用性能、提升用户体验的重要性,并展望了未来技术趋势下Android的发展方向。 ####
64 0
|
3月前
|
存储 缓存 算法
提高 Java 数组性能的方法
【10月更文挑战第19天】深入探讨了提高 Java 数组性能的多种方法。通过合理运用这些策略,我们可以在处理数组时获得更好的性能表现,提升程序的运行效率。
52 2
|
3月前
|
存储 Java
Java“(array) <X> Not Initialized” (数组未初始化)错误解决
在Java中,遇到“(array) &lt;X&gt; Not Initialized”(数组未初始化)错误时,表示数组变量已被声明但尚未初始化。解决方法是在使用数组之前,通过指定数组的大小和类型来初始化数组,例如:`int[] arr = new int[5];` 或 `String[] strArr = new String[10];`。
114 2

推荐镜像

更多