AndroidStudio下JNI开发(easy+1.0)之字符串转换关系

来源:互联网 发布:win10删除多余的网络2 编辑:程序博客网 时间:2024/05/22 12:03

几个提示,直接c++回调java
env->GetObjectClass
env->GetMethodID
env->NewStringUTF
env->CallVoidMethod


类型转换表

JAVA基础类型与C++之间的对应表

Java类型C/C++类型描述booleanjboolean无符号8位整数bytejbyte无符号8位整数charjchar有符号16位整数shortjshort有符号16位整数intjint有符号32位整数longjlong有符号64位整数floatjfloat32位单精度浮点数doublejdouble64位双精度浮点数
这一点可以从jni.h头文件中定义看到:
typedef unsigned char   jboolean;       /* unsigned 8 bits */typedef signed char     jbyte;          /* signed 8 bits */typedef unsigned short  jchar;          /* unsigned 16 bits */typedef short           jshort;         /* signed 16 bits */typedef int             jint;           /* signed 32 bits */typedef long long       jlong;          /* signed 64 bits */typedef float           jfloat;         /* 32-bit IEEE 754 */typedef double          jdouble;        /* 64-bit IEEE 754 */
由上面的JNI定义代码可以看到,基础类型都是根据Java类型大小等价转换的。因此在使用Java传递过来的基础类型或者返回Java中的基础类型都可以直接使用。比如:
JNIEXPORT jint JNICALLJava_com_example_wastrel_test_Test_BaseTypeTest(JNIEnv *env, jclass type,jint i) {    return i+5;}

Java引用类型与C++之间的对应表

Java类型C/C++类型描述Objecjobject任何Java对象ClassjclassClass类对象StringjstringString类对象Object[]jobjectArray对象数组boolean[]jbooleanArray布尔数组byte[]jbyteArray字节数组char[]jcharArray字符型数组short[]jshortArray短整型数组int[]jintArray整型数组long[]jlongArray长整型数组float[]jfloatArray浮点型数组double[]jdoubleArray双精度浮点型数组通过jni里的定义可以看到,任何引用类型(数组在Java中也是引用传递的)传递到C++里面只是一个指向Java对象的指针。因此引用类型不能直接使用,JNIEnv提供了大量的方法来完成了他们之间的转换。
/* * Reference types, in C++ */class _jobject {};class _jclass : public _jobject {};class _jstring : public _jobject {};class _jarray : public _jobject {};class _jobjectArray : public _jarray {};class _jbooleanArray : public _jarray {};class _jbyteArray : public _jarray {};class _jcharArray : public _jarray {};class _jshortArray : public _jarray {};class _jintArray : public _jarray {};class _jlongArray : public _jarray {};class _jfloatArray : public _jarray {};class _jdoubleArray : public _jarray {};class _jthrowable : public _jobject {};typedef _jobject*       jobject;typedef _jclass*        jclass;typedef _jstring*       jstring;typedef _jarray*        jarray;typedef _jobjectArray*  jobjectArray;typedef _jbooleanArray* jbooleanArray;typedef _jbyteArray*    jbyteArray;typedef _jcharArray*    jcharArray;typedef _jshortArray*   jshortArray;typedef _jintArray*     jintArray;typedef _jlongArray*    jlongArray;typedef _jfloatArray*   jfloatArray;typedef _jdoubleArray*  jdoubleArray;typedef _jthrowable*    jthrowable;typedef _jobject*       jweak;

使用JNIEnv完成数据之间的转换

这里只说明引用对象之间的转换,因为对于普通类型,Java与C++是互通的。

String的传递

这里将String单独拿出来讲解,因为String在Java中有着超高的使用频次,JNIEnv对String有相对应的转换函数。JNI里的函数定义如下:

/**转换成unicode相关函数 */jstring     NewString(const jchar*, jsize);jsize       GetStringLength(jstring);const jchar* GetStringChars(jstring, jboolean* isCopy);void        ReleaseStringChars(jstring, const jchar*);/** 转换成UTF-8相关函数 */jstring     NewStringUTF(const char*);jsize       GetStringUTFLength(jstring);const char* GetStringUTFChars(jstring, jboolean* isCopy);void        ReleaseStringUTFChars(jstring, const char*);


当从 JNI 函数 GetStringChars中返回得到字符串B时,如果B是原始字符串java.lang.String 的拷贝,则isCopy被赋值为JNI_TRUE。如果B和原始字符串指向的是JVM中的同一份数据,则 isCopy被赋值为 JNI_FALSE。

当isCopy值为JNI_FALSE时,本地代码决不能修改字符串的内容,否则JVM中的原始字符串也会被修改,这会打破 JAVA语言中字符串不可变的规则。 
通常,因为你不必关心JVM是否会返回原始字符串的拷贝,你只需要为 isCopy传递NULL作为参数。


由以上代码,我们可以清晰的看到String处理函数分为了Unicode和UTF-8两种编码类型,但在Android中使用的UTF-8相关的函数,两种编码提供的功能都基本一致,从函数名就可以看出对应函数的功能。下面通过一个使用C++合并String的例子来演示如何使用:
/Java中定义Native函数 public native static String strConcat(String str1,String str2);//生成C++函数并用C++实现字符串连接功能#include "string.h"JNIEXPORT jstring JNICALL Java_com_example_wastrel_test_Test_strConcat        (JNIEnv *env, jclass clazz, jstring str1, jstring str2){        //将jstring转换成const char*指针,使用const修饰符表示其内容不可被修改        const char* c1=env->GetStringUTFChars(str1, NULL);        const char* c2=env->GetStringUTFChars(str2, NULL);        //计算新字符串的长度        int size=strlen(c1)+strlen(c2);        //创建一个新的字符串,这里长度+1是为了使字符串有结尾标记'\0'        char * n_char=new char[size+1];        //利用C标准库提供的字符串操作方法对字符串进行连接,这里需要include"string.h"头文件        strcpy(n_char,c1);        strcat(n_char,c2);        //将生成的新字符串转换成UTF的jstring        jstring rs=env->NewStringUTF(n_char);        //删除刚刚分配的内存 避免引起内存泄漏        delete [] n_char;        //通知JVM虚拟机Native代码不在持有字符串的引用,说明白点,就是告诉虚拟机我不使用它了,你可以回收了。        //因为在JVM中如果对象被引用,那么对象将不会被回收。        //这里为什么要传递jstring和生成的char*呢?是因为char*有可能是jstring的拷贝,如果是拷贝,那么char*就应该被删除。        env->ReleaseStringUTFChars(str1,c1);        env->ReleaseStringUTFChars(str2,c2);        return rs;}
//然后我们在Java中调用该函数print(Test.strConcat("里约奥运",",中国加油"));
注:本处以及往后代码中使用的print函数仅仅是把结果追加显示在界面上的TextView上代码如下:
private void print(String str){    tv.append(str+"\n");}
运行结果: 
运行结果

基础数据的数组传递

数组传递跟字符串传递一样,Native收到的都是引用的形式,因此JNIEnv也提供了一些列的方法来完成数据的转换。因为不同数据类型之间的调用方式基本一致,此处使用int型数组作为讲解,int[]传递到Native后收到的是jintArray对象。

//获得数组的长度,该方法适用于所有jarray对象jsize GetArrayLength(jarray array)//在本地创建一个jint数组,这个数组只能通过SetIntArrayRegion赋值。jintArray NewIntArray(jsize length);//将jintArray转换成jint指针jint* GetIntArrayElements(jintArray array, jboolean* isCopy);//取出数组中的部分元素放在buf里void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf);//给jintArray按区间赋值void SetIntArrayRegion(jintArray array, jsize start, jsize len,const jint* buf);//释放jntArray,第一个参数表示传过来的jintArray,第二参数表示获取到的本地数组指针//第三个参数需要重点说明,该参数有三个取值:0、JNI_COMMIT、JNI_ABORT//取值 零(0) 时,更新数组并释放所有元素;//取值 JNI_COMMIT 时,更新但不释放所有元素;//取值 JNI_ABORT 时,不作更新但释放所有元素;//一般实际应用中取0较多void ReleaseIntArrayElements(jintArray array, jint* elems,jint mode);

例子1:来自JNI的int数组

//Java中定义Native函数,size表示返回数组的大小public native static int[] getIntArray(int size);//生成Native函数并实现方法#include "stdlib.h"#include "time.h"//定义随机数产生宏 表示产生0~x之间的随机数#define random(x) (rand()%x)JNIEXPORT jintArray JNICALL Java_com_example_wastrel_test_Test_getIntArray        (JNIEnv *env, jclass clazz, jint size){        //用时间变量初始化随机数产生器        srand((int)time(0));        jint* rs=new jint[size];        for (int i=0;i<size;i++)        {            //调用宏产生0~100的随机数            rs[i]=random(100);        }        //通过JNIEnv的NewIntArray方法new一个jintArray对象        jintArray array=env->NewIntArray(size);        //把产生的随机数值赋值给jintArray        env->SetIntArrayRegion(array,0,size,rs);        return array;}
//Java中调用函数int []rs=Test.getIntArray(10);print("来自于JNI的Int数组");print(IntArrayToString(rs));/**将int[]转换成逗号分隔便于显示的辅助函数*/private String IntArrayToString(int[] ints){    StringBuilder str=new StringBuilder();    str.append('[');    for (int i:ints)    {        str.append(i);        str.append(',');    }    str.deleteCharAt(str.length()-1);    str.append(']');    return str.toString();}

运行结果: 
这里写图片描述

例子2:使用JNI对例1返回的数组进行排序

//声明Java Native函数,参数为int[] public native static void sortIntArray(int []ints);//实现C++函数JNIEXPORT void JNICALL Java_com_example_wastrel_test_Test_sortIntArray        (JNIEnv *env, jclass clazz, jintArray array){        //获得传递过来的数组长度        jsize size=env->GetArrayLength(array);        //将数组转换成Java指针        jint* jints=env->GetIntArrayElements(array,NULL);        //简单的冒泡排序        for (int i = 0; i <size-1 ; ++i) {                for (int j = 0; j <size-1-i ; ++j) {                        if(jints[j]<jints[j+1])                        {                                int t=jints[j];                                jints[j]=jints[j+1];                                jints[j+1]=t;                        }                }        }        //将排序结果更新到Java数组中,第三个参数等于0表明更新到原数组并释放所有元素        env->ReleaseIntArrayElements(array,jints,0);        return;}//在Java中调用print("通过JNI对int数组排序:");Test.sortIntArray(rs);print(IntArrayToString(rs));这里可以看到,我们并没有返回数组,而是通过ReleaseIntArrayElements函数将结果更新到Java数组中。随之我们在Java中的数组值已经变更。

运行结果: 
这里写图片描述

其他基础类型数组的传递

其他基础类型中的数组传递与int[]几乎一致,函数名更换成各自的类型即可。这里不在过多叙述,我想有了上面的例子,很容易明白别的基础数据类型传递使用。


/转载请注明出处:http://blog.csdn.net/venusic/article/details/52187452


0 0
原创粉丝点击