未公开的mustang核心秘密(二):jni的返回中文问题
严格的说,我基本没有做过jni的开发,只是修改过bsd和windows下的jdk源码。这些是openjdk的jni部分函数,这函数完全用c写的,幸好不是c++,没有用复杂的设计模式,我还看得懂。下面这段代码,从控制台通过jni输入字符串,java里面打印
classPrompt {
// native method that prints a prompt and reads a line
privatenative String getLine(String prompt);
public static void main(String args[]) {
Prompt p = newPrompt();
String input = p.getLine("Type a line: ");
System.out.println("User typed: " + input);
}
static {
System.loadLibrary("jnistudy");
}
}
promt.c的代码
#include <jni.h>
#include<stdio.h>
#include "Prompt.h"
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj,jstring prompt)
{
char buf;
const char *str;
str =(*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL){
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt,str);
/* We assume here that the user does not type more than
*127 characters */
scanf("%s", buf);
return(*env)->NewStringUTF(env,buf);
}
如果控制台输入中文,java是unicode编码,到jni是utf8编码,输出肯定是乱码。
如果单纯是转化中文,也是很麻烦的,http://blogger.org.cn/blog/more.asp?name=hongrui&id=25509使用的办法也是麻烦。
这是jdk用于转码的函数
JNIEXPORT jstring
NewStringPlatform(JNIEnv *env, const char*str)
{
return JNU_NewStringPlatform(env, str);
}
JNIEXPORT jstring JNICALL
JNU_NewStringPlatform(JNIEnv *env, const char*str)
{
jstring result;
jbyteArray hab = 0;
int len;
if (fastEncoding == NO_ENCODING_YET)
initializeEncoding(env);
if ((fastEncoding == FAST_8859_1) || (fastEncoding ==NO_ENCODING_YET))
return newString8859_1(env, str);
if(fastEncoding == FAST_646_US)
return newString646_US(env,str);
if (fastEncoding == FAST_CP1252)
returnnewStringCp1252(env, str);
if((*env)->EnsureLocalCapacity(env, 2) < 0)
return 0;
len = (int)strlen(str);
hab = (*env)->NewByteArray(env,len);
if (hab != 0) {
(*env)->SetByteArrayRegion(env, hab,0, len, (jbyte *)str);
if (jnuEncodingSupported(env)){
result = (*env)->NewObject(env, JNU_ClassString(env),
String_init_ID, hab, jnuEncoding);
}else {
/*If the encoding specified in sun.jnu.encoding is notendorsed
by "Charset.isSupported" we have to fall back to useString(byte[])
explicitly here without specifying the encodingname, in which the
StringCoding class will pickup theiso-8859-1 as the fallback
converter for us.
*/
jmethodID mid = (*env)->GetMethodID(env,JNU_ClassString(env),
"<init>", "([B)V");
result =(*env)->NewObject(env, JNU_ClassString(env), mid, hab);
}
(*env)->DeleteLocalRef(env, hab);
return result;
}
return 0;
}
里面调用了initializeEncoding
J/* Initialize the fast encoding.If the "sun.jnu.encoding" property
* has not yet been set, we leavefastEncoding == NO_ENCODING_YET.
*/
staticvoid
initializeEncoding(JNIEnv *env)
{
jstring propname = 0;
jstring enc = 0;
if ((*env)->EnsureLocalCapacity(env, 3) < 0)
return;
propname = (*env)->NewStringUTF(env, "sun.jnu.encoding");
if(propname) {
jboolean exc;
enc =JNU_CallStaticMethodByName
(env,
&exc,
"java/lang/System",
"getProperty",
"(Ljava/lang/String;)Ljava/lang/String;",
propname).l;
if(!exc) {
if (enc) {
const char* encname =(*env)->GetStringUTFChars(env, enc, 0);
if (encname) {
/*
* On Solaris with nl_langinfo() called in GetJavaProperties():
*
* locale undefined -> NULL -> hardcoded default
* "C"locale -> "" -> hardcoded default(on 2.6)
* "C"locale -> "ISO646-US"(on Sol 7/8)
* "en_US" locale ->"ISO8859-1"
* "en_GB" locale -> "ISO8859-1"(on Sol 7/8)
* "en_UK" locale -> "ISO8859-1"(on 2.6)
*/
if((strcmp(encname, "8859_1") == 0) ||
(strcmp(encname, "ISO8859-1") == 0) ||
(strcmp(encname, "ISO8859_1") == 0))
fastEncoding =FAST_8859_1;
else if (strcmp(encname, "ISO646-US") ==0)
fastEncoding = FAST_646_US;
else if(strcmp(encname, "Cp1252") == 0 ||
/* This is atemporary fix until we move */
/* to widecharacter versions of all Windows */
/* calls.*/
strcmp(encname,"utf-16le") == 0)
fastEncoding =FAST_CP1252;
else {
fastEncoding = NO_FAST_ENCODING;
jnuEncoding =(jstring)(*env)->NewGlobalRef(env, enc);
}
(*env)->ReleaseStringUTFChars(env, enc, encname);
}
}
} else{
(*env)->ExceptionClear(env);
}
} else {
(*env)->ExceptionClear(env);
}
(*env)->DeleteLocalRef(env,propname);
(*env)->DeleteLocalRef(env, enc);
/* Initialize method-id cache */
String_getBytes_ID =(*env)->GetMethodID(env, JNU_ClassString(env),
"getBytes","(Ljava/lang/String;)[B");
String_init_ID = (*env)->GetMethodID(env,JNU_ClassString(env),
"<init>","([BLjava/lang/String;)V");
}
static jboolean isJNUEncodingSupported =JNI_FALSE;
static jboolean jnuEncodingSupported(JNIEnv *env) {
jboolean exe;
if (isJNUEncodingSupported == JNI_TRUE) {
returnJNI_TRUE;
}
isJNUEncodingSupported = (jboolean)JNU_CallStaticMethodByName (
env,&exe,
"java/nio/charset/Charset",
"isSupported",
"(Ljava/lang/String;)Z",
jnuEncoding).z;
returnisJNUEncodingSupported;
}
上面的promt.c代码改为
#include<jni.h>
#include <stdio.h>
#include "Prompt.h"
#include"jni_util.h"
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj,jstring prompt)
{
char buf;
const char *str;
int len= (*env)->GetStringLength(env, prompt);
printf("%d\n",len);
str =(*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL){
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt,str);
/* We assume here that the user does not type more than
*127 characters */
scanf("%s", buf);
return NewStringPlatform(env,buf);
}
运行Prompt.class,输入
邢红瑞
User typed: 邢红瑞
13
Type a line:
这就解决了中文平台java和jni的转码问题。
页:
[1]