1、背景

今天调试了一下Android jni关于Java中调用C代码的程序,发现我的数组参数传递方式不对,导致值传递不正确,我的方法是:

C代码,入口函数

#include

#include

jint Java_sony_MedicalRecordDemo_MainActivity_decryptionSuccess(JNIEnv* env, jobject thiz,jint Attr[])

{

return Attr[0];

}

java代码,调用

package sony.MedicalRecordDemo;

import android.app.Activity;

import android.os.Bundle;

public class MainActivity extends Activity {

private static final String libSoName = "NDKtest";

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

initViews();

}

private void initViews() {

int Attr[] = {0,0,1,1,0};

int result = decryptionSuccess(Attr);

System.out.println(result);

}

public native int decryptionSuccess(int[] Attr);

static {

System.loadLibrary(libSoName);

}

}

返回结果:1073819256,明显值没有传到C代码。

2、问题所在及解决方法

查看了一些关于android jni参数传递方面的资料,发现问题出在C代码中的

jint Java_sony_MedicalRecordDemo_MainActivity_decryptionSuccess(JNIEnv* env, jobject thiz,jint Attr[])

红色的地方。

解决方法是:

#include

#include

jint Java_sony_MedicalRecordDemo_MainActivity_decryptionSuccess(JNIEnv* env, jobject thiz,jintArray Attr)

{

jint* arr;

jint length;

arr = (*env)->GetIntArrayElements(env,Attr,NULL);

length = (*env)->GetArrayLength(env,Attr);

return arr[0];

}

3、注意GetIntArrayElements的用法

第一种:

env->GetIntArrayElements(array1,NULL);

我用这种方法的时候,用ndk命令编译时出错了。

第二种:

(*env)->GetIntArrayElements(env,nums, isCopy) , 返回 所有数据。If isCopy is not NULL, then *isCopy is set to JNI_TRUE if a copy ismade; if no copy is made, it is set to JNI_FALSE.

貌似ndk命令只支持这个方式吧,我不太确定,还请高人指点。

4、反思与总结

JNI数组传递与异常处理

JNI通过JNIEnv提供的操作Java数组的功能。它提供了两个函数:一个是操作java的简单型数组的,另一个是操作对象类型数组的。

因为速度的原因,简单类型的数组作为指向本地类型的指针暴露给本地代码。因此,它们能作为常规的数组存取。这个指针是指向实际的Java数组或者Java数组的拷贝的指针。另外,数组的布置保证匹配本地类型。

为了存取Java简单类型的数组,你就要要使用GetXXXArrayElements函数(见表A),XXX代表了数组的类型。这个函数把Java数组看成参数,返回一个指向对应的本地类型的数组的指针。

表A函数Java 数组类型本地类型

GetBooleanArrayElementsjbooleanArrayjboolean

GetByteArrayElementsjbyteArrayjbyte

GetCharArrayElementsjcharArrayjchar

GetShortArrayElementsjshortArrayjshort

GetIntArrayElementsjintArrayjint

GetLongArrayElementsjlongArrayjlong

GetFloatArrayElementsjfloatArrayjfloat

GetDoubleArrayElementsjdoubleArrayjdouble

JNI数组存取函数

当你对数组的存取完成后,要确保调用相应的ReleaseXXXArrayElements函数,参数是对应Java数 组和GetXXXArrayElements返回的指针。如果必要的话,这个释放函数会复制你做的任何变化(这样它们就反射到java数组),然后释放所 有相关的资源。

为了使用java对象的数组,你必须使用GetObjectArrayElement函数和SetObjectArrayElement函数,分别去get,set数组的元素。GetArrayLength函数会返回数组的长度。

下面通过一个实际的例子,演示一下在JAVA中传递基本类型的数组与对象类型的数组,然后在C++中进行相应的处理。

数组传递:

JAVA中的代码:

package com.cjz.ibm;

public class CopyArray {

static int totalsum = 0;

static int a[] = new int[] { 1, 2, 3, 4, 5 };

static String str[] = new String[] { "we", "are", "friends" };

static {

System.loadLibrary("CopyArray");

}

private native int sum(int[] num);

private native int sum(String[] str);

public static void main(String[] args) {

CopyArray cp = new CopyArray();

cp.sum(a);

cp.sum(str);

}

}

在这个简单的java程序中,我们定义了两种类型的数组,一种是整形数组,属于基本数据类型的数组,另一种是字符串类型 的数组,属于对象数组。然后把这两种类型数组分别作为参数传递到本地方法sum中去。其中sum函数具有相同的函数名和返回值类型,区别它们的是参数类 型,这样,就涉及到方法签名的问题,方法签名是参数的类型+方法的返回值类型。可知,它们的方法签名是不相同的,所以为两个不同的方法。

C++代码:

#include

#include

#include "com_cjz_ibm_CopyArray.h"

JNIEXPORT jint JNICALL Java_com_cjz_ibm_CopyArray_sum___3I

(JNIEnv* env, jobject obj, jintArray array1){

//传入的参数是整形数组

jint* arr;//定义一个整形指针

int sum=0;

//对于整形数组的处理,主要有GetIntArrayElements与GetIntArrayRegion

//第一种方法

arr=env->GetIntArrayElements(array1,NULL);//得到一个指向原始数据类型内容的指针

jint length=env->GetArrayLength(array1);//得到数组的长度

int i=0;

for(i=0;i

cout<

sum+=arr[i];

}

//第二种方法

jint buf[]={0,0,0,0,0};//定义一个jint类型的buffer把原始的数组copy到这个buf中去

env->GetIntArrayRegion(array1,0,length,buf);

for(i=0;i

cout<

sum+=buf[i];

}

//返回一个jint类型的数组

//可以先往一个数组中输入值,然后把这个数组copy到jintArray中

jintArray iarr =env->NewIntArray(length);//新建一个jintArray

env->SetIntArrayRegion(iarr, 0, length, buf);//将buf中的值复制到jintArray中去,数组copy

//打印新的数组值

jint* arr2;

arr2=env->GetIntArrayElements(iarr,NULL);

for(i=0;iGetArrayLength(iarr);i++){

cout<

}

return sum;

}

JNIEXPORT jint JNICALL Java_com_cjz_ibm_CopyArray_sum___3Ljava_lang_String_2

(JNIEnv* env, jobject obj, jobjectArray array2){

//在java中,String[]类型是对象,所以对应C++中的数组为jobjectArray

//把jobjectArray数组中的值取出来

int size=env->GetArrayLength(array2);//得到数组的长度值

int i=0;

cout<

for(i=0;i

jstring obja=(jstring)env->GetObjectArrayElement(array2,i);

const char* chars=env->GetStringUTFChars(obja,NULL);//将jstring类型转换成char类型输出

cout<

}

//复制数组到新的数组中

cout<

jclass objClass = env->FindClass("java/lang/String");//定义数组中元素类型

jobjectArray texts= env->NewObjectArray(size, objClass, 0);//创建一个数组类型为String类型

jstring jstr;

for(i=0;i

{

jstr=(jstring)env->GetObjectArrayElement(array2,i);//把array2中的元素取出来

env->SetObjectArrayElement(texts, i, jstr);//放入到texts数组中去,必须放入jstring

}

for(i=0;i

jstring str1=(jstring)env->GetObjectArrayElement(texts,i);//打印出新复制的数组值

const char* chars1=env->GetStringUTFChars(str1,NULL);

cout<

}

return 0;

}

在C++代码中,int类型的数组对应JNI中的jintArray,而字符串类型的数组对应jobjectArray.

首先分析一下代码:

GetIntArrayElements(array,jboolean *isCopy):第一个参数传递的是数组名,第二个参数传递的是否返回一个复制的数组。返回值:返回一个指向数组的指针。

GetIntArrayRegion(array,jsize start,jsize len, *buf)

array:a reference to an array whose elements are to be copied.

start:the starting index of the array elements to be copied.

len: the number of elements to be copied.

buf: the destination buffer.

功能:把jintArray中的元素复制到buffer中。

SetIntArrayRegion(array,jsize start,jsize len,*buf)

array:a reference to a primitive array to which the elements to be copied.

start:the starting index in the primitive array.

len:the number of elements to be copied.

buf:the source buffer.

功能:把buf中的元素copy到jintArray中去。

可见,SetIntArrayRegion与GetIntArrayRegion是设置jintArray数组与取出jintArray数组中的值。

NewIntArray(jsize length)

length:the number of elements in the array to be created.

功能:构造一个数组对象

返回值:返回一个jintArray类型。

GetArrayLength(array)

返回值:返回jintArray的长度。

由于在C++中,jintArray不能用下标直接存取,所以用到JNI中提供的接口函数进行操作。

GetObjectArrayElement(jobjectArray array,jsize index)

array: a reference to the java.lang.Object array from which the element will be accessed.

index: the array index

功能:返回对应索引值的object.返回的是一个数组元素的值。

SetObjectArrayElement(jobjectArray array,jsize index,jobject value)

array: a reference to an array whose element will be accessed.

index: index of the array element to be accessed.

value: the new value of the array element.

功能:用来设置对应索引元素的值。

因此,对于基本类型的数组,我们可以用GetArrayElements和GetArrayRegion来操作。

而SetArrayRegion传递一个预先设置好的buf来返回一个 jArray.而对于jobjectArray,可以用SetObjectArrayElement与 GetObjectArrayElement来设置元素的值与返回元素的值。

对于数组的操作就介绍到这了。

异常处理:

下面看一个简单的例子:

JAVA代码:

ackage com.ibm.cjz;

public class Exception1 {

private native void doit();

private void callback() {

int a[] = new int[3];

System.out.println(a[5]);// 数组越界,在C++中会发生异常,然后C++中把这个异常传递给java

}

public static void main(String[] args) {

try {

Exception1 ex = new Exception1();

ex.doit();

} catch (Exception ex) {

System.out.println(ex);

}

}

static {

System.loadLibrary("Exception");

}

}

在这个JAVA代码中,定义了一个整形数组,然后在callback()方法中调用这个数组,可以看到数组越界了,下面演示在C++中如何捕获这个异常。

C++代码:

#include "com_ibm_cjz_Exception1.h"

#include

#include

JNIEXPORT void JNICALL Java_com_ibm_cjz_Exception1_doit

(JNIEnv* env, jobject obj){

jclass cls=env->GetObjectClass(obj);

jmethodID mid=env->GetMethodID(cls,"callback","()V");

env->CallVoidMethod(obj,mid);

//调用callback方法,获取异常信息

jthrowable excp=0;

const char* str;

excp=env->ExceptionOccurred();//得到异常对象

if(excp){

jclass cls=env->GetObjectClass(excp);

//env->ExceptionClear();

//env->ExceptionDescribe();

jmethodID mid=env->GetMethodID(cls,"toString","()Ljava/lang/String;");//调用String中的toString方法

jstring msg=(jstring)env->CallObjectMethod(excp,mid);

str=env->GetStringUTFChars(msg,NULL);

cout<

env->ExceptionClear();//把这个异常清除掉,使之不能传递到java

}

jclass errclass;

errclass=env->FindClass("java/lang/ArrayIndexOutOfBoundsException");

env->ThrowNew(errclass, "thrown from C++ code");//向java中抛出异常

}

jthrowable ExceptionOccurred(JNIEnv *env);

返回值:返回一个异常对象。

void ExceptionClear(JNIEnv *env);

功能:清除该异常对象,只在本线程中传递。

jint ThrowNew(JNIEnv *env, jclass clazz,

const char *message);

功能:抛出一个指定的异常对象和异常信息。

打印结果:

In C:java.lang.ArrayIndexOutOfBoundsException: 5

java.lang.ArrayIndexOutOfBoundsException: thrown from C++ code

第一行是C++中打印的信息,第二行是从C++中抛出的异常。

如果把 env->ExceptionClear();//把这个异常清除掉,使之不能传递到java

jclass errclass;

errclass=env->FindClass("java/lang/ArrayIndexOutOfBoundsException");

env->ThrowNew(errclass, "thrown from C++ code");//向java中抛出异常

都注释掉,也会向JAVA中抛出异常。这时就没有清除掉异常了。

总之,数组的传递与异常在处理在JNI中非常的重要,在这里只是简单的介绍一下,欢迎大家共同学习。

c++ java setobjectarrayelement_Android jni中数组参数的传递方式相关推荐

  1. java中字符串和数组如何比较_[Java教程]javascript中数组和字符串的方法比较

    [Java教程]javascript中数组和字符串的方法比较 0 2016-07-19 23:00:05 ×目录[1]可索引 [2]转换 [3]拼接[4]创建[5]位置 前面的话 字符串和数组有很多的 ...

  2. java从配置文件中读取参数

    java从配置文件中读取参数的方式 有的时候我们需要将一些固定的配置信息写到yml文件中去,然后在类中在读取 #首先就得先在yml文件中配置参数 upload:param:"test&quo ...

  3. Java笔记-JNI中Java与C语言解决中文乱码问题

    目录 基本概念 演示过程 基本概念 这里我把他总结成4个步骤: 1. 找类,Java的String: 2. 找函数String类的方法,成员函数: 3. 设置要转换的字符和编码: 4. 程序生成编码后 ...

  4. Java在方法调用时参数的传递方式有且仅有只有值传递

    1. 值传递 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数. 2. 引用传递 是指在调用函数时,将实际参数的地址传递到函数中,那么在函数中 ...

  5. python函数中可变参数的传递方式是_Python中函数的参数传递与可变长参数

    1.Python中也有像C++一样的默认缺省函数 1 def foo(text,num=0):2 printtext,num3 4 foo("asd") #asd 0 5 foo( ...

  6. Java笔记-JNI中简单的参数与返回值处理

    目录 基本概念 代码及演示 基本概念 此篇博文记录了JNI的基本规范,与使用: https://blog.csdn.net/qq78442761/article/details/94849374 在本 ...

  7. java 参数值_Java中的参数传值方式

    本文转载自 https://blog.csdn.net/SEU_Calvin/article/details/70089977 1.  你觉得下面程序会输出什么 public static void  ...

  8. python kotlin_在Python,Java和Kotlin中标记参数和重载

    python kotlin 在多种语言之间跳来跳去可以帮助您注意到不同语言的习惯用法和最佳做法之间的某些差异. 比较有趣的差异之一与一个函数执行多项操作有关. Python 我们先来看一下Python ...

  9. 在Python,Java和Kotlin中标记参数和重载

    在多种语言之间跳来跳去可以帮助您注意到不同语言的习惯用法和最佳做法之间的某些差异. 比较有趣的差异之一与一个函数执行多项操作有关. Python 我们先来看一下Python. Python实际上无法重 ...

最新文章

  1. 计算机视觉:让冰冷的机器看懂多彩的世界
  2. 今日arXiv精选 | 28篇EMNLP 2021最新论文
  3. 最长公共子序列Lcs 51Nod - 1006
  4. Android之打开手机系统相册
  5. React Native之hardwareBackPress
  6. C语言实例 区分旅客国籍
  7. amp sqlserver中 什么意思_股票术语中的做空到底是什么意思?
  8. 有时候,拒绝是为了更好的未来
  9. GD32 NAND U盘
  10. 【项目介绍】单发动机驱动的多旋翼飞行器及其控制系统
  11. 通信感知一体化(ISAC)概述
  12. 【文学】平凡的世界第三部
  13. 记一个悲剧的英文逻辑题笔试
  14. 移动端适配之一:到底什么是像素
  15. 记一次微信公众号开发过程
  16. IDEA打包时clean报错
  17. javaString-StringBuilder-StringBuffer
  18. GO+MySQL,如虎添翼!
  19. WORD排版技巧(一)
  20. include,include_once,require,require_once的区别

热门文章

  1. 以EV录屏为例详细讲解-录屏,开直播的全局配置
  2. 龙年的喜洋洋真的很烂
  3. 文字图片行内垂直居中对齐方法
  4. 标题标签<h1></h1>和段落标签<p>
  5. 运营商大数据丨电销行业如何高效获得精准客户
  6. Asp.Net Core 鉴权授权
  7. mount:special device does not exist (a path prefix is not a directory)
  8. 安卓手机怎么下载java游戏
  9. TS+vue3 页面红色波浪线(和声明类型有关)
  10. 耐心维修日本原装进口,美国优派气象专用显示器4k