首先,需要明确几个关于字符编码的基本概念:
◆ java内部是使用的16bit的unicode编码(utf-16)来表示字符串的,无论英文还是中文都是2字节;
◆
jni内部是使用utf-8编码来表示字符串的,utf-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
◆
c/c++使用的是原始数据,ascii就是一个字节,中文一般是GB2312编码,用2个字节表示一个汉字。
jni的中文字符串处理
先从字符流的方向分别对java-->C++和C++-->java进行分析
◆
java-->C++
这种情况下,java调用的时候使用的是utf-16编码的字符串,jvm把这个参数传递给jni,C++得到的输入是jstring,此时,可以利用jni提供的两种函数,一个是GetStringUTFChars,这个函数将得到一个UTF-8编码的字符串;另一个是GetStringChars这个将得到UTF-16编码的字符串。无论那个函数,得到的字符串如果含有中文,都需要进一步转化成GB2312的编码。
◆ c/c++ –> java
jni返回给java的字符串,c/c++首先应该负责把这个字符串变成UTF-8或者UTF-16格式,然后通过NewStringUTF或者NewString来把它封装成jstring,返回给java就可以了。
如果字符串中不含中文字符,只是标准的ascii码,那么用GetStringUTFChars/NewStringUTF就可以搞定了,因为这种情况下,UTF-8编码和ascii编码是一致的,不需要转换。
但是如果字符串中有中文字符,那么在c/c++部分进行编码转换就是一个必须了。我们需要两个转换函数,一个是把UTF8/16的编码转成GB2312;一个是把GB2312转成UTF8/16。
下面是一个测试DEMO,native代码( NDK samples下hellojni ) : (注意红色部分代码及API)
#include
#include
#include
#include
#include
#include
#include
#define LOG_TAG
"hellojni_native"
#define LOGI(...)
((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG,
__VA_ARGS__))
#define LOGE(...)
((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
__VA_ARGS__))
//Java字符串的类和获取方法ID
jclass
gStringClass;
jmethodID gmidStringInit;
jmethodID
gmidStringGetBytes;
int isASCII(const char *
chp)
{
char
ch;
jboolean flag= 1;
while(ch = *chp++){
if(ch & 0x80){
flag = 0;
break;
}
}
return flag;
}
// jstring to char*
char* JstringToPchar(JNIEnv* env,
jstring jstr, const char * encoding)
{
char* rtn = NULL;
jstring jencoding;
jencoding=(*env)->NewStringUTF(env,encoding);
jbyteArray barr=
(jbyteArray)(*env)->CallObjectMethod(env,jstr,
gmidStringGetBytes, jencoding);
jsize alen =
(*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr,
JNI_FALSE);
if (alen > 0)
{
//LOGI("alen = %d ",alen);
rtn
= (char*)malloc( alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env,barr, ba,
0);
return rtn;
}
//char* to jstring
jstring PcharToJstring(JNIEnv* env,
const char* pchar,const char * encoding)
{
jstring jencoding;
jbyteArray bytes =
(*env)->NewByteArray(env,strlen(pchar));
(*env)->SetByteArrayRegion(env,bytes, 0,
strlen(pchar), (jbyte*)pchar);
jencoding =
(*env)->NewStringUTF(env,encoding);
return (jstring)(*env)->NewObject(env,gStringClass,
gmidStringInit, bytes, jencoding);
}
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv*
env,jobject thiz )
{
jstring result;
gStringClass=
(*env)->FindClass(env,"java/lang/String");
gmidStringGetBytes=
(*env)->GetMethodID(env,gStringClass, "getBytes",
"(Ljava/lang/String;)[B");
gmidStringInit=
(*env)->GetMethodID(env,gStringClass, "",
"([BLjava/lang/String;)V");
//char *myTest = "Hello from JNI !";
char *myTest = "[华仔]Hello from JNI !";
if(isASCII(myTest)) {
LOGI("Is
ASCII");
result = (*env)->NewStringUTF(env,
myTest);
}
else{
LOGI("Is
Not ASCII");
result
= PcharToJstring(env,myTest,"gbk");
char *outbuf;
outbuf = JstringToPchar(env,result,"utf-8");
//LOGI("strlen(outbuf) = %d",strlen(outbuf));
//int i;
//for(i = 0 ; i < strlen(outbuf);i++)
//
LOGI("outbuf[%d] = %x",i,outbuf[i]);
result =
(*env)->NewStringUTF(env, outbuf);
if(outbuf){
free(outbuf);
}
}
return result;
}
测试结果: