Android JNI 中文字符传递

JNI使用时,需要传递中文,遇到乱码问题,多次尝试失败。遂仔细研究了下编码格式。
首先,需要明确几个关于字符编码的基本概念:
◆ java内部是使用的16bit的unicode编码(utf-16)来表示字符串的,无论英文还是中文都是2字节;
◆ jni内部是使用utf-8编码来表示字符串的,utf-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
◆ c/c++使用的是原始数据,ascii就是一个字节,中文一般是GB2312编码,用2个字节表示一个汉字。

C语言中的汉字传递到java端

先来实现C语言中的汉字传递到java端,有两种方案:

  • 1,在C端以字节传送到java端,在java中转换;
  • 2,在C端就转换好。

下面来详细说明:

1,在C端以字节传送到java端,在java中转换编码格式;

在C中,将char*转换为jbyteArray,然后设置数据:

jbyteArray byteArray = env->NewByteArray(strlen(str));
env->SetByteArrayRegion(byteArray,0,strlen(str),(jbyte *)str);

在Java端,获取数组,然后从”gb2312”格式生成utf16格式的java字符串:

byte[] byteArray = JniClient.getChineseByteArray();
strSayHello = new String(byteArray,"gb2312");

2,在C端就转换好编码格式

步骤要复杂一些,核心还是一样的,是在C端通过jni访问java的String创建方法,进行编码格式转换。

jstring CStr2Jstring(JNIEnv*   env,   char* buf)
{
    jclass Class_string;
    jmethodID mid_String, mid_getBytes;
    jbyteArray bytes;
    jbyte* log_utf8;
    jstring codetype, jstr;
    Class_string = env->FindClass( "java/lang/String"); //获取class
    //先将gbk字符串转为java里的string格式
    mid_String = env->GetMethodID( Class_string, "<init>",
            "([BLjava/lang/String;)V");
    int len=strlen(buf)+1;//需要加1,把字符串的结束符也包含进来
    bytes = env->NewByteArray( len);
    env->SetByteArrayRegion( bytes, 0, len, (jbyte*) buf);
    codetype = env->NewStringUTF( "gbk");
    jstr = (jstring)env->NewObject( Class_string, mid_String, bytes, codetype);
    env->DeleteLocalRef( bytes);

    //再将string变utf-8字符串。
    mid_getBytes = env->GetMethodID( Class_string, "getBytes",  "(Ljava/lang/String;)[B");
    codetype = env->NewStringUTF( "utf-8");
    bytes = (jbyteArray)env->CallObjectMethod( jstr, mid_getBytes, codetype);
    log_utf8 = env->GetByteArrayElements( bytes, JNI_FALSE);
    return env->NewStringUTF((char *)log_utf8);
}

java中的汉字传递到C端

下面来实现java中的汉字传递到C端,也是两种方案:

  • 1,在Java端就转换好编码格式。
  • 2,java端只传递,在C端转换;

下面来详细说明:

1,在Java端就转换好编码格式

在Java中,将String转换为”gbk”格式的byte[]:

String strToJNI = "欢迎";
byte[] byteArr = new byte[strToJNI.length()*2];
byteArr=strToJNI.getBytes("gbk");

在C中,获取jbyteArray的起始地址,当成char*的地址访问即可:

jbyte * byteArr=env->GetByteArrayElements(byteArray,0);
char * buf = (char *)byteArr;

2,java端只传递,在C端转换;

在C语言中做编码格式的转换,步骤要复杂一些,但是核心思想还是一样的,就是在C端通过jni访问java的String创建方法,进行编码格式转换。

char*   Jstring2CStr(JNIEnv*   env,   jstring   jstr)
{
     char*   rtn   =   NULL;
     jclass   clsstring   =   env->FindClass("java/lang/String");//寻找 java里面String.class
     jstring   strencode   =   env->NewStringUTF("GB2312");//创建java字符串 "gb2312"
     jmethodID   mid   =   env->GetMethodID(clsstring,   "getBytes",   "(Ljava/lang/String;)[B");//寻找到java String getbytes();
     jbyteArray   barr=   (jbyteArray)env->CallObjectMethod(jstr,mid,strencode); // String .getByte("GB2312");
     jsize   alen   =   env->GetArrayLength(barr); //获取长度
     jbyte*   ba   =   env->GetByteArrayElements(barr,JNI_FALSE); 

//jbyteArray转为jbyte*
     if(alen   >   0)
     {
      rtn   =   (char*)malloc(alen+1);         //"\0"
      memcpy(rtn,ba,alen);
      rtn[alen]=0;
     }
     env->ReleaseByteArrayElements(barr,ba,0);  //释放掉
     return rtn;
}

总结

1,优先选择在java端做字符编码格式的转换

通过上面的介绍,我们可以看出,在Java端实现编码格式的转换,代码更简洁,也更易于理解。所以,我们应该优先选择在java端做字符编码格式的转换。

2,C中字符串的长度

在C中向外传递时需要加1,将结束符传递出来(见CStr2Jstring())。否则,后面不知道会跟着什么随机数据的。
在java向C传递的byte数组,逐字节打印数组内容,数组的长度不能使用strlen(buf)(见SetChineseByteArray())。
传递的是byte数组,而不是字符串,所以这个长度不能使用strlen,而是需要调用env->GetArrayLength(byteArray)来获取。

3,C中的char类型

在C语言中调用日志打印函数,中文打印为乱码。
我是通过打印具体字节数据来验证传递成功的。
打印语句如下:

LOGI( "log: buf[%d]=%d",i,*(signed char *)(buf+i));

为何要强制转换为 signed char 呢?
我之前未使用强制转换时,打印出来的数据,并不一致,如下:
06-26 16:35:50.220: I/Hello(4529): log: byteArr[0]=-60
06-26 16:35:50.220: I/logfromc(4529): log: buf[0]=196
这两个数据有差异的原因在于:java中的byte,取值范围为[-128,127]。
在ARM平台上,C语言中的char的取值范围[0,255],也就是说,arm上的char是无符号的。通过计算:-60=196-256,可以知道值是匹配的。但是这样靠人脑计算比较累,所以就让电脑干啦(强制转换成 signed char)。
通常我们的手机的cpu都是ARM,例如我的手机就是arm啦,所以用char类型打印出来的值是不同的。
在模拟器上测试,C语言中的char是有符号的,取值范围[-128,127],因为模拟器是在PC上跑的,PC是x86平台。
char是有点特殊的啦,在不同平台上编译出来的情况,有所不同,可要注意哦。

demo下载:

http://download.csdn.net/detail/lintax/9560249

你可能感兴趣的:(java,c,android,jni,中文)