• JNI数组传递与异常处理


    原文:http://blog.csdn.net/chenjin_zhong/article/details/5872566

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

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

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

    表A

    函数 Java 数组类型 本地类型
    GetBooleanArrayElements jbooleanArray jboolean
    GetByteArrayElements jbyteArray jbyte
    GetCharArrayElements jcharArray jchar
    GetShortArrayElements jshortArray jshort
    GetIntArrayElements jintArray jint
    GetLongArrayElements jlongArray jlong
    GetFloatArrayElements jfloatArray jfloat
    GetDoubleArrayElements jdoubleArray jdouble

    JNI数组存取函数

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

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

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

    数组传递: 

    1. package com.cjz.ibm;

    2. public class CopyArray {
    3.  static int totalsum = 0;
    4.  static int a[] = new int[] { 1, 2, 3, 4, 5 };
    5.  static String str[] = new String[] { "we", "are", "friends" };
    6.  static {

    7.   System.loadLibrary("CopyArray");

    8.  }

    9.  private native int sum(int[] num);

    10.  private native int sum(String[] str);

    11.  public static void main(String[] args) {

    12.   CopyArray cp = new CopyArray();

    13.   cp.sum(a);
    14.   cp.sum(str);

    15.  }

    16. }

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

    经过编译,生成C++的头文件,这个过程可以参考http://blog.csdn.net/chenjin_zhong/archive/2010/09/08/5870305.aspx  

    1. #include <iostream.h>
    2. #include <string.h>
    3. #include "com_cjz_ibm_CopyArray.h"
    4. JNIEXPORT jint JNICALL Java_com_cjz_ibm_CopyArray_sum___3I
    5. (JNIEnv* env, jobject obj, jintArray array1){
    6. //传入的参数是整形数组

    7. jint* arr;//定义一个整形指针
    8. int sum=0;
    9. //对于整形数组的处理,主要有GetIntArrayElements与GetIntArrayRegion
    10. //第一种方法
    11. arr=env->GetIntArrayElements(array1,NULL);//得到一个指向原始数据类型内容的指针
    12. jint length=env->GetArrayLength(array1);//得到数组的长度

    13. int i=0;
    14. for(i=0;i<length;i++){

    15.  cout<<"arr["<<i<<"]="<<arr[i]<<endl;
    16.  sum+=arr[i];
    17. }


    18. //第二种方法
    19. jint buf[]={0,0,0,0,0};//定义一个jint类型的buffer把原始的数组copy到这个buf中去
    20. env->GetIntArrayRegion(array1,0,length,buf);

    21. for(i=0;i<length;i++){

    22.  cout<<"buf["<<i<<"]="<<buf[i]<<endl;
    23.  sum+=buf[i];
    24. }


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

    26. //可以先往一个数组中输入值,然后把这个数组copy到jintArray中
    27. jintArray iarr =env->NewIntArray(length);//新建一个jintArray
    28. env->SetIntArrayRegion(iarr, 0, length, buf);//将buf中的值复制到jintArray中去,数组copy

    29. //打印新的数组值
    30. jint* arr2;
    31. arr2=env->GetIntArrayElements(iarr,NULL);
    32. for(i=0;i<env->GetArrayLength(iarr);i++){

    33.    cout<<"arr2["<<i<<"]="<<arr2[i]<<endl;

    34. }



    35. return sum;


    36. }

    37. JNIEXPORT jint JNICALL Java_com_cjz_ibm_CopyArray_sum___3Ljava_lang_String_2
    38. (JNIEnv* env, jobject obj, jobjectArray array2){
    39. //在java中,String[]类型是对象,所以对应C++中的数组为jobjectArray

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

    41.  int size=env->GetArrayLength(array2);//得到数组的长度值
    42.  int i=0;
    43.  cout<<"输出原始的数组值:"<<endl;
    44.  for(i=0;i<size;i++){
    45.  jstring obja=(jstring)env->GetObjectArrayElement(array2,i);
    46.  const char* chars=env->GetStringUTFChars(obja,NULL);//将jstring类型转换成char类型输出
    47.  cout<<chars<<endl;

    48.  }

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

    50. cout<<"复制之后的数组为:"<<endl;

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

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

    53.    jstring jstr;

    54.        for(i=0;i<size;i++)

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

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

    59.        }

    60.  for(i=0;i<size;i++){

    61.  jstring str1=(jstring)env->GetObjectArrayElement(texts,i);//打印出新复制的数组值
    62.  const char* chars1=env->GetStringUTFChars(str1,NULL);
    63.  cout<<chars1<<endl;

    64.  }
    65.      
    66. return 0;

    67. }

     

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

    首先分析一下代码:

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

    GetIntArrayRegion(<ArrayType>array,jsize start,jsize len,<NativeType> *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(<ArrayType>array,jsize start,jsize len,<NativeType>*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(<ArrayType>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.

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

    因此,对于基本类型的数组,我们可以用Get<Type>ArrayElements和Get<Type>ArrayRegion来操作。

    而Set<Type>ArrayRegion传递一个预先设置好的buf来返回一个j<Type>Array.而对于jobjectArray,可以用SetObjectArrayElement与GetObjectArrayElement来设置元素的值与返回元素的值。

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

    异常处理:

    下面看一个简单的例子:

    1. package com.ibm.cjz;

    2. public class Exception1 {

    3.  private native void doit();

    4.  private void callback() {

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

    7.  }

    8.  public static void main(String[] args) {

    9.   try {
    10.    Exception1 ex = new Exception1();

    11.    ex.doit();
    12.   } catch (Exception ex) {

    13.    System.out.println(ex);

    14.   }

    15.  }

    16.  static {

    17.   System.loadLibrary("Exception");

    18.  }
    19. }

     

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

    1. #include "com_ibm_cjz_Exception1.h"
    2. #include <iostream.h>
    3. #include <string.h>
    4. JNIEXPORT void JNICALL Java_com_ibm_cjz_Exception1_doit
    5. (JNIEnv* env, jobject obj){

    6. jclass cls=env->GetObjectClass(obj);
    7. jmethodID mid=env->GetMethodID(cls,"callback","()V");
    8. env->CallVoidMethod(obj,mid);
    9. //调用callback方法,获取异常信息

    10. jthrowable excp=0;
    11. const char* str;
    12. excp=env->ExceptionOccurred();//得到异常对象
    13. if(excp){

    14.     jclass cls=env->GetObjectClass(excp);
    15.     //env->ExceptionClear();
    16.     //env->ExceptionDescribe();
    17.     jmethodID mid=env->GetMethodID(cls,"toString","()Ljava/lang/String;");//调用String中的toString方法
    18.     jstring msg=(jstring)env->CallObjectMethod(excp,mid);
    19.     str=env->GetStringUTFChars(msg,NULL);
    20.     cout<<"In C:"<<str<<endl;
    21.     env->ExceptionClear();//把这个异常清除掉,使之不能传递到java
    22. }
    23. jclass errclass;
    24. errclass=env->FindClass("java/lang/ArrayIndexOutOfBound***ception");
    25. env->ThrowNew(errclass, "thrown from C++ code");//向java中抛出异常

    26. }

    27. jthrowable ExceptionOccurred(JNIEnv *env);


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

    void ExceptionClear(JNIEnv *env);

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

    jint ThrowNew(JNIEnv *env, jclass clazz,
    const char *message);

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

    打印结果:

    In C:java.lang.ArrayIndexOutOfBound***ception: 5
    java.lang.ArrayIndexOutOfBound***ception: thrown from C++ code

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

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

    jclass errclass;
    errclass=env->FindClass("java/lang/ArrayIndexOutOfBound***ception");
    env->ThrowNew(errclass, "thrown from C++ code");//向java中抛出异常

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

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

    阅读(1162) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    有关Fragment的知识点
    DashPathEffect
    Android 自定义View
    Android Parcelable
    Android之Activity状态的保存和恢复
    Android WebView介绍
    了解Android的编译器
    WebView
    使用RichEdit程序无法启动
    WTL布局对话框 CDialogResize
  • 原文地址:https://www.cnblogs.com/black/p/5171792.html
Copyright © 2020-2023  润新知