Android JNI 数组操作_weixin_30241919的博客-程序员宅基地

技术标签: java  移动开发  c/c++  

JNI 中有两种数组操作,基础数据类型数组和对象数组,JNI 对待基础数据类型数组和对象数组是不一样的。

基本数据类型数组

对于基本数据类型数组,JNI 都有和 Java 相对应的结构,在使用起来和基本数据类型的使用类似。

在 Android JNI 基础知识篇提到了 Java 数组类型对应的 JNI 数组类型。比如,Java int 数组对应了 jintArray,boolean 数组对应了 jbooleanArray。

如同 String 的操作一样,JNI 提供了对应的转换函数:GetArrayElements、ReleaseArrayElements。

    intArray = env->GetIntArrayElements(intArray_, NULL);
    env->ReleaseIntArrayElements(intArray_, intArray, 0);

另外,JNI 还提供了如下的函数:

  • GetTypeArrayRegion / SetTypeArrayRegion

将数组内容复制到 C 缓冲区内,或将缓冲区内的内容复制到数组上。

  • GetArrayLength

得到数组中的元素个数,也就是长度。

  • NewTypeArray

返回一个指定数据类型的数组,并且通过 SetTypeArrayRegion 来给指定类型数组赋值。

  • GetPrimitiveArrayCritical / ReleasePrimitiveArrayCritical

如同 String 中的操作一样,返回一个指定基础数据类型数组的直接指针,在这两个操作之间不能做任何阻塞的操作。

实际操作如下:

    // Java 传递 数组 到 Native 进行数组求和
    private native int intArraySum(int[] intArray, int size); 

对应的 C++ 代码如下:

JNIEXPORT jint JNICALL
Java_com_glumes_cppso_jnioperations_ArrayTypeOps_intArraySum(JNIEnv *env, jobject instance,
                                                             jintArray intArray_, jint num) {
    jint *intArray;
    int sum = 0; // 操作方法一: // 如同 getUTFString 一样,会申请 native 内存 intArray = env->GetIntArrayElements(intArray_, NULL); if (intArray == NULL) { return 0; } // 得到数组的长度 int length = env->GetArrayLength(intArray_); LOGD("array length is %d", length); for (int i = 0; i < length; ++i) { sum += intArray[i]; } LOGD("sum is %d", sum); // 操作方法二: jint buf[num]; // 通过 GetIntArrayRegion 方法来获取数组内容 env->GetIntArrayRegion(intArray_, 0, num, buf); sum = 0; for (int i = 0; i < num; ++i) { sum += buf[i]; } LOGD("sum is %d", sum); // 使用完了别忘了释放内存 env->ReleaseIntArrayElements(intArray_, intArray, 0); return sum; } 

假如需要从 JNI 中返回一个基础数据类型的数组,对应的代码如下:

    // 从 Native 返回基本数据类型数组
    private native int[] getIntArray(int num); 

对应的 C++ 代码如下:

/**
 * 从 Native 返回 int 数组,主要调用 set<Type>ArrayRegion 来填充数据,其他数据类型类似操作
 */
extern "C"
JNIEXPORT jintArray JNICALL Java_com_glumes_cppso_jnioperations_ArrayTypeOps_getIntArray(JNIEnv *env, jobject instance, jint num) { jintArray intArray; intArray = env->NewIntArray(num); jint buf[num]; for (int i = 0; i < num; ++i) { buf[i] = i * 2; } // 使用 setIntArrayRegion 来赋值 env->SetIntArrayRegion(intArray, 0, num, buf); return intArray; } 

以上例子,基本把相关的操作都使用上了,可以发现和 String 的操作大都是相似的。

对象数组

对于对象数组,也就是引用类型数组,数组中的每个类型都是引用类型,JNI 只提供了如下函数来操作。

  • GetObjectArrayElement / SetObjectArrayElement

和基本数据类型不同的是,不能一次得到数据中的所有对象元素或者一次复制多个对象元素到缓冲区。只能通过上面的函数来访问或者修改指定位置的元素内容。

字符串和数组都是引用类型,因此也只能通过上面的方法来访问。

例如在 JNI 中创建一个二维的整型数组并返回:

    // 从 Native 返回二维整型数组,相当于是一个一维整型数组,数组中的每一项内容又是数组
    private native int[][] getTwoDimensionalArray(int size); 

二维数组具有特殊性在于,可以将它看成一维数组,其中数组的每项内容又是一维数组。

具体 C++ 代码如下:

/**
 * 从 Native 返回一个二维的整型数组
 */
extern "C"
JNIEXPORT jobjectArray JNICALL Java_com_glumes_cppso_jnioperations_ArrayTypeOps_getTwoDimensionalArray(JNIEnv *env, jobject instance, jint size) { // 声明一个对象数组 jobjectArray result; // 找到对象数组中具体的对象类型,[I 指的就是数组类型 jclass intArrayCls = env->FindClass("[I"); if (intArrayCls == NULL) { return NULL; } // 相当于初始化一个对象数组,用指定的对象类型 result = env->NewObjectArray(size, intArrayCls, NULL); if (result == NULL) { return NULL; } for (int i = 0; i < size; ++i) { // 用来给整型数组填充数据的缓冲区 jint tmp[256]; // 声明一个整型数组 jintArray iarr = env->NewIntArray(size); if (iarr == NULL) { return NULL; } for (int j = 0; j < size; ++j) { tmp[j] = i + j; } // 给整型数组填充数据 env->SetIntArrayRegion(iarr, 0, size, tmp); // 给对象数组指定位置填充数据,这个数据就是一个一维整型数组 env->SetObjectArrayElement(result, i, iarr); // 释放局部引用 env->DeleteLocalRef(iarr); } return result; } 

首先需要使用 NewObjectArray 方法来创建对象数组。

然后使用 SetObjectArrayElement 函数填充数据时,需要构建好每个位置对应的对象。这里就使用了 NewIntArray 来创造了一个对象,并给对象填充数据后,在赋值给对象数组。

通过一个 for 循环就完成给对象数组赋值的操作。

在创建对象数组时,有一个操作是找到对应的对象类型,通过 findClass 方法。findClass 的参数 [I 这里就涉及到 Java 与 JNI 对应签名的转换。

Java 与 JNI 签名的转换

在前一篇文章中,用表格列出了 Java 与 JNI 对应的数据类型格式的转换关系,现在要列举的是 Java 与 JNI 对应签名的转换关系。

这里的签名指的是在 JNI 中去查找 Java 中对应的数据类型、对应的方法时,需要将 Java 中的签名转换成 JNI 所能识别的。

对于类的签名转换

对于 Java 中类或者接口的转换,需要用到 Java 中类或者接口的全限定名,把 Java 中描述类或者接口的 . 换成 / 就好了,比如 String 类型对应的 JNI 描述为:

java/lang/String     // . 换成 / 

对于数组类型,则是用 [ 来表示数组,然后跟一个字段的签名转换。

[I      // 代表一维整型数组,I 表示整型
[[I     // 代表二维整型数组
[Ljava/lang/String;      // 代表一维字符串数组, 
对于字段的签名转换

对应基础类型字段的转换:

Java 类型 JNI 对应的描述转
boolean Z
byte B
char C
short S
int I
long J
float F
double D

对于引用类型的字段签名转换,是大写字母 L 开头,然后是类的签名转换,最后以 ; 结尾。

Java 类型 JNI 对应的描述转换
String Ljava/lang/String;
Class Ljava/lang/Class;
Throwable Ljava/lang/Throwable
int[] "[I"
Object[] "[Ljava/lang/Object;"
对于方法的签名转换

对于方法签名描述的转换,首先是将方法内所有参数转换成对应的字段描述,并全部写在小括号内,然后在小括号外再紧跟方法的返回值类型描述。

Java 类型 JNI 对应的描述转换
String f(); ()Ljava/lang/String;
long f(int i, Class c); (ILjava/lang/Class;)J
String(byte[] bytes); ([B)V

这里要注意的是在 JNI 对应的描述转换中不要出现空格。

了解并掌握这些转换后,就可以进行更多的操作了,实现 Java 与 C++ 的相互调用。

比如,有一个自定义的 Java 类,然后再 Native 中打印类的对象数组的某一个字段值。

    private native void printAnimalsName(Animal[] animal); 

具体 C++ 代码如下:

/**
 * 打印对象数组中的信息
 */
extern "C"
JNIEXPORT void JNICALL Java_com_glumes_cppso_jnioperations_ArrayTypeOps_printAnimalsName(JNIEnv *env, jobject instance, jobjectArray animals) { jobject animal; // 数组长度 int size = env->GetArrayLength(animals); // 数组中对应的类 jclass cls = env->FindClass("com/glumes/cppso/model/Animal"); // 类对应的字段描述 jfieldID fid = env->GetFieldID(cls, "name", "Ljava/lang/String;"); // 类的字段具体的值 jstring jstr; // 类字段具体值转换成 C/C++ 字符串 const char *str; for (int i = 0; i < size; ++i) { // 得到数组中的每一个元素 animal = env->GetObjectArrayElement(animals, i); // 每一个元素具体字段的值 jstr = (jstring) (env->GetObjectField(animal, fid)); str = env->GetStringUTFChars(jstr, NULL); if (str == NULL) { continue; } LOGD("str is %s", str); env->ReleaseStringUTFChars(jstr, str); } } 

具体示例代码可参考我的 Github 项目,欢迎 Star。

https://github.com/glumes/AndroidDevWithCpp

转载于:https://www.cnblogs.com/Free-Thinker/p/10602008.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30241919/article/details/99071743

智能推荐

linux如何查看网口ip_Linux 常用命令_weixin_39846898的博客-程序员宅基地

阅读文本大概需要 3 分钟。自己开始接触 Linux「Ubuntu」应该是在大学期间, 接触 Unix「Mac OSX」是在工作后的第一份工作,吭哧吭哧的用了好几年的 Linux,强行此文,自己总结了目前经常用到的几个命令,希望给 Linux 用户提供帮助。如果没有 Linux 的,也可以在 win10 的 Store 上面直接安装 Ubuntu,win7 用户也可以安装 GIT 或者 Cygwi...

程序员必背单词_荚小白的博客-程序员宅基地

-- =======================A=============================abstract 抽象的abstract base class (ABC)抽象基类abstract class 抽象类abstraction 抽象、抽象物、抽象性access 存取、访问access function 访问函数access level访问级别account...

【Error】git push时出现 fatal: unable to access ‘xxx‘: OpenSSL SSL_read: Connection was reset_MangoloD的博客-程序员宅基地

今天在使用git的时候出现了点小问题,即git push origin masterfatal: unable to access 'https://github.com/MangoloD/Attention.git/': OpenSSL SSL_read: Connection was reset, errno 10054可以看到我在使用git push命令时,报错啦产生原因:一般是这是因为服务器的SSL证书没有经过第三方机构的签署,所以才报错查看了网上的解决方法,记录一下:方案一g

开源整理:酷酷的Android Loading动画,让用户摆脱无聊等待_技术视界的博客-程序员宅基地

一个出色的应用常常会用心打磨各种交互细节,例如为了能够让用户在某些耗时操作的等待过程中不要感到那么无聊,加上一个有趣的Loading动画效果将会是一个不错的选择。今天就为大家推荐Github上几个效果比较酷的优质开源项目,让你的用户可以打发无聊的等待时间,体验更上一层楼。AVLoadingIndicatorViewhttps://github.com/81813780/AVLoadingIndic

05-树9 Huffman Codes_王寒寒的博客-程序员宅基地

05-树9 Huffman Codes来自:PTA_数据结构_Huffman CodesIn 1953, David A. Huffman published his paper “A Method for the Construction of Minimum-Redundancy Codes”, and hence printed his name in the history of co...

随便推点

运行项目编译报错_du_90的博客-程序员宅基地

在编译项目的时候XIB报这种错误Internal error. Please file a bug at bugreport.apple.com and attach “/var/folders/2v/krsnqt7j7bdfpg8zg_x0cszc0000gn/T/IB-agent-diagnostics_2015-11-16_09-11-23_017000”.开始的时候以为是动了xib文件就

mac下安装mbstring_你是我的绝笔的博客-程序员宅基地

今天写php一个查询模块,需要从数据库读取一段内容,但是不要太多。只要截取部分即可,于是想到用mb_substr函数,但是发现不支持这个函数,于是去网上找教程,在http://stackoverflow.com/questions/4787403/missing-mbstring-php-extension-when-installing-phpmyadmin-on-mac中找到

Valine评论系统不能使用_画江湖之不浪人的博客-程序员宅基地

1、Valine 评论系统不能使用这几天发现 Valine 评论系统不见了,没法使用啦,发现原来是themes / next1 / layout / _third-party / comments / valine.swig这个文件网页找不到的问题2、原因官方也给了说法,是因为 leancloud.cn 以及 ..lncld.net 域名不能解析了,官方说明链接 见图3、解决方案我...

ecmall数据字典_weixin_30703911的博客-程序员宅基地

ecm_acategory //文章分类表字段类型Null默认注释cate_idint(10)否自增ID号,分类ID号cate_namevarchar(100)否分类的名称...

又被限速,我决定用 Serverless 搭建一款私人网盘_Serverless的博客-程序员宅基地

网盘限速的痛苦大家都体会过,这个问题除了充钱,其实就可以用 Serverless 来解决。GitHub 上就有一个 OneManager 项目,能将 Onedrive 打造成为个人网盘。通过本文,你将能学会如何部署该应用,就三步,快试一试!项目地址:https://github.com/qkqpttgf/OneManager-phpOneManager 是什么OneManager-php 是一款强大的 OneDrive 列表程序,它利用微软 Onedrive api 将 Onedrive 目录映射成一

python中format函数用法简书_Python str.format() 使用_weixin_39841002的博客-程序员宅基地

1. Basic usage>>> print('{} {}'.format('hello','world'))hello world>>> print('{1} {1} and {0}'.format('hello','world'))world world and hello>>> print('{a} {tom} {a}'.format(tom='hello',a='world')) #可...

推荐文章

热门文章

相关标签