六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 40|回复: 0

未公开的mustang核心秘密(二):jni的返回中文问题

[复制链接]

升级  53.65%

673

主题

673

主题

673

主题

探花

Rank: 6Rank: 6

积分
2073
 楼主| 发表于 2013-1-26 14:09:28 | 显示全部楼层 |阅读模式
严格的说,我基本没有做过jni的开发,只是修改过bsd和windows下的jdk源码。这些是openjdk的jni部分函数,这函数完全用c写的,幸好不是c++,没有用复杂的设计模式,我还看得懂。
下面这段代码,从控制台通过jni输入字符串,java里面打印
class  Prompt {
// native method that prints a prompt and reads a line
private  native String getLine(String prompt);
public static void main(String args[]) {
Prompt p = new  Prompt();
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[128];
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)
return  newStringCp1252(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 not  endorsed
by "Charset.isSupported" we have to fall back to use  String(byte[])
explicitly here without specifying the encoding  name, in which the
StringCoding class will pickup the  iso-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 leave  fastEncoding == NO_ENCODING_YET.
*/
static  void
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 a  temporary fix until we move */
/* to wide  character 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) {
return  JNI_TRUE;
}
isJNUEncodingSupported = (jboolean)  JNU_CallStaticMethodByName (
env,  &exe,
  "java/nio/charset/Charset",
  "isSupported",
  "(Ljava/lang/String;)Z",
  jnuEncoding).z;
return  isJNUEncodingSupported;
}
上面的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[128];
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的转码问题。
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表