android jni返回字符串数组的字符串是乱码怎么办

Android(15)
C/C++(14)
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*
char* buf)
jclass Class_
jmethodID mid_String, mid_getB
jbyteArray bytes;
jbyte* log_utf8;
jstring codetype,
Class_string = env-&FindClass( "java/lang/String");
mid_String = env-&GetMethodID( Class_string, "&init&",
"([BLjava/lang/S)V");
int len=strlen(buf)+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);
mid_getBytes = env-&GetMethodID( Class_string, "getBytes",
"(Ljava/lang/S)[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 *)byteA
2,java端只传递,在C端转换;
在C语言中做编码格式的转换,步骤要复杂一些,但是核心思想还是一样的,就是在C端通过jni访问java的String创建方法,进行编码格式转换。
Jstring2CStr(JNIEnv*
env-&FindClass("java/lang/String");
env-&NewStringUTF("GB2312");
env-&GetMethodID(clsstring,
"getBytes",
"(Ljava/lang/S)[B");
jbyteArray
(jbyteArray)env-&CallObjectMethod(jstr,mid,strencode);
env-&GetArrayLength(barr);
env-&GetByteArrayElements(barr,JNI_FALSE);
(char*)malloc(alen+1);
memcpy(rtn,ba,alen);
rtn[alen]=0;
env-&ReleaseByteArrayElements(barr,ba,0);
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下载:
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:67627次
积分:1169
积分:1169
排名:千里之外
原创:46篇
评论:29条
(2)(8)(6)(1)(1)(4)(1)(1)(2)(4)(4)(2)(1)(1)(1)(1)(1)(2)(5)(2)android-JNI(11)
android 高级技术(31)
1、创建页面
C语言调用java反射方法
* @项目名称
com.android.imooc.jni
ReflectActivity
日 上午8:49:35
public class ReflectActivity extends Activity {
System.loadLibrary("hello");
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jni_reflect);
public void reflect(View view){
showFromC();
public void show(String msg){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("反射方法调用");
builder.setMessage(msg);
builder.show();
public native void showFromC();
2、在Hello.c里创建方法
JNIEXPORT void Java_com_android_imooc_jni_ReflectActivity_logFromC(JNIEnv* env,
jobject obj) {
3、获得java里 show方法的签名
1)首先进入到项目的classes目录 E:\androidproject\App_imooc\bin\classes
2)右键+shift打开窗口, 如图:
3)点击“在此处打开命令窗口”
4)输入E:\androidproject\App_imooc\bin\classes&
javap -s com.android.imooc.jni.ReflectAc
5)得到签名:
(Ljava/lang/S)V
4、在C方法里先得到反编译类
jclass clazz = ( *env)-&FindClass(env, "com/android/imooc/jni/ReflectActivity");
//得到方法名称
( *env)-&GetMethodID(env, clazz, "show", "(Ljava/lang/S)V");
//把C字符串转化为java字符串
jstring str = (*env)-&NewStringUTF(env,"hello world");
//调用回调方法
( *env)-&CallVoidMethod(env, obj, id, str);
5、Hello.c完整代码
JNIEXPORT void JNICALL Java_com_android_imooc_jni_ReflectActivity_showFromC(JNIEnv* env,
jobject obj) {
//拿到字节吗
jclass clazz = ( *env)-&FindClass(env, "com/android/imooc/jni/ReflectActivity");
//获得方法id
//jmethodID
(*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID id = ( *env)-&GetMethodID(env, clazz, "show", "(Ljava/lang/S)V");
(*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
jstring str = (*env)-&NewStringUTF(env,"hello world");
( *env)-&CallVoidMethod(env, obj, id, str);
6、最后调用成后的效果:
7、注意,如果是中文,小心乱码问题,解决办法:
右键hello.c文件–》属性–》选择UTF-8编码
8、Java自身反射简单示例:
package com.java.basic.
import java.lang.reflect.InvocationTargetE
import java.lang.reflect.M
* @项目名称
com.java.basic.reflect
SimpleDemo
日 上午8:41:48
public class SimpleDemo {
public void show(String str){
System.out.println(str);
public void reflect(){
Class clazz = SimpleDemo.class.getClassLoader().loadClass("com.java.basic.reflect.SimpleDemo");
Method method = clazz.getDeclaredMethod("show", String.class);
method.invoke(clazz.newInstance(), "hello world");
} catch (Exception e) {
e.printStackTrace();
public static void main(String[] args) {
SimpleDemo demo = new SimpleDemo();
demo.reflect();
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:67474次
积分:4218
积分:4218
排名:第4973名
原创:373篇
评论:22条android JNI开发 javah生成C/C++头文件 命令行乱码_百度知道
android JNI开发 javah生成C/C++头文件 命令行乱码
com/zhidao/wh%3D600%2C800/sign=9a50df959d16fdfad839cee884bfa06c/eaf81a4c510fd9f9eadd42a
其他类似问题
为您推荐:
javah的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁JNI char*转jstring乱码问题
[问题点数:100分,结帖人Java_Devil]
JNI char*转jstring乱码问题
[问题点数:100分,结帖人Java_Devil]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
本帖子已过去太久远了,不再提供回复功能。您所在的位置: &
JNI和C++通信中文乱码的问题
JNI和C++通信中文乱码的问题
bluesky的博客
从Java1.1开始,JNI标准成为Java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
首先,需要明确几个关于编码的基本概念:
◆& 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的编码。
&&&&&&&String &&&&&&&(UTF-16) &&&&&&&&&&&| &[java]&&&&| &--------------------&&JNI&调用 &[cpp]&&&&&| &&&&&&&&&&&v &&&&&&&&jstring &&&&&&&&(UTF-16) &&&&&&&&&&&|&&& &&+--------+---------+& &&|GetStringChars&&&&|GetStringUTFChars &&|&&&&&&&&&&&&&&&&&&| &&v&&&&&&&&&&&&&&&&&&v &wchar_t*&&&&&&&&&&&char* &(UTF_16)&&&&&&&&&&&(UTF-8)&
◆& c/c++ && java
jni返回给java的字符串,c/c++首先应该负责把这个字符串变成UTF-8或者UTF-16格式,然后通过NewStringUTF或者NewString来把它封装成jstring,返回给java就可以了。
&&&&&&&String &&&&&&&(UTF-16)& &&&&&&&&&&&^& &&&&&&&&&&&| &[java]&&&&| &--------------------&&JNI&返回 &[cpp]&&&&&|&&&&&&&jstring &&&&&&&&(UTF-16) &&&&&&&&&&&^ &&&&&&&&&&&| &&+--------+---------+ &&^&&&&&&&&&&&&&&&&&&^ &&|&&&&&&&&&&&&&&&&&&| &&|NewString&&&&&&&&&|NewStringUTF &wchar_t*&&&&&&&&&&char* &(UTF_16)&&&&&&&&&&(UTF-8)&
如果字符串中不含中文字符,只是标准的ascii码,那么用GetStringUTFChars/NewStringUTF就可以搞定了,因为这种情况下,UTF-8编码和ascii编码是一致的,不需要转换。
但是如果字符串中有中文字符,那么在c/c++部分进行编码转换就是一个必须了。我们需要两个转换函数,一个是把UTF8/16的编码转成GB2312;一个是把GB2312转成UTF8/16。
这里要说明一下:linux和win32都支持wchar,这个事实上就是宽度为16bit的unicode编码UTF16,所以,如果我们的 c/c++程序中完全使用wchar类型,那么理论上是不需要这种转换的。但是实际上,我们不可能完全用wchar来取代char的,所以就目前大多数应用而言,转换仍然是必须的。
具体的转换函数,linux和win32都有一定的支持,比如glibc的mbstowcs就可以用来把 GB2312编码转成UTF16,但是这种支持一般是平台相关的(因为c/c++的标准中并没有包括这部分),不全面的(比如glibc就没有提供转成 UTF8的编码),不独立的(linux下mbstowcs的行为要受到locale设置的影响)。所以我推荐使用iconv库来完成转换。
iconv库是一个免费的独立的编码转换库,支持很多平台,多种编码(事实上,它几乎可以处理我们所使用的所有字符编码),而且它的行为不受任何外部环境的影响。iconv在*nix平台上,基本上是缺省安装的。在win32平台上需要额外安装。
下面提供一个把GB2312编码的字符串转换成UTF8编码的示例
#include&&iconv.h&char*&BytesToUtf8(string&src,&char*&dst,&int*&nout)&{&&&&size_t&n_in&=&src.length();&&&&size_t&n_out&=&*&&&&&&&&iconv_t&c&=&iconv_open(&UTF-8&,&&GB2312&);&&&&if&(c&==&(iconv_t)-1)&{&&&&&&&&cerr&&&&strerror(errno)&&&&&&&&&&&&return&NULL;}& && && &char*&inbuf&=&new&char&[n_in&+&1]; &&if&(!inbuf)&{ &&iconv_close(c); &&return&NULL; &&} && &strcpy(inbuf,&src.c_str()); &&memset(dst,&0,&n_out); && &char*&in&=& &&char*&out&=& &&if&(iconv(c,&&in,&&n_in,&&out,&&n_out)&==&(size_t)-1)&{ &&cerr&&&&strerror(errno)&&&& &&out&=&NULL; &&} &&else&{ &&n_out&=&strlen(dst); &&out&=& &&} && &iconv_close(c); &&*nout&=&n_ &&delete[]& && &return& &}&
补充几点说明:
1、从jni的接口看,jni提供了UTF16和UTF8两个系列的字符串处理函数,但是由于jni的文档中说,jni的内部实现中是用UTF8作为字符串编码格式的,所以使用UTF8系列比较合适(NewStringUTF/GetStringUTFChars /ReleaseStringUTFChars)
2、使用iconv库的话,运行环境的设置对于编码转换是没有影响的,但是外层java程序对于字符串的解析依赖于运行环境的locale,所以设置正确的locale对于jni意义不大,但是对整个系统还是必要的。
以上是主要是说明使用第三方库去解决编码问题,针对仅在windows平台下,是可以使用windows提供的相关方法进行编码转换的。
使用一下方法可以将jstring转换为char*,主要用于在C++中接收java传递过来的参数时包含中文字符时使用。在转换过程中已经对编码进行了转换,可以正常返回出中文字符。
char&*&JStringToWindows(JNIEnv&*&pJNIEnv,&jstring&jstr) &{ &&&&&jsize&len&=&pJNIEnv-&GetStringLength(jstr); &&&&&const&jchar&*&jcstr&=&pJNIEnv-&GetStringChars(jstr,&NULL); &&&&&int&size&=&0; &&&&&char&*&str&=&(char&*)malloc(len&*&2&+&1); &&&&&if&((size&=&WideCharToMultiByte(CP_ACP,&0,&LPCWSTR(jcstr),&len,&str,&len&*&2&+&1,&NULL,&NULL))&==&0) &&&&&&&&&return&NULL; &&&&&pJNIEnv-&ReleaseStringChars(jstr,&jcstr); &&&&&str[size]&=&0; &&&&&return& &}&
注意:以上方法中返回的char*在使用过后需要delete释放,因为在编码转换过程中使用了malloc分配了内存,不释放会发生内存泄漏。
如果需要在C++中返回中文信息给java,则需要以下方法将char*转换成jstring。
jstring&WindowsTojstring(&JNIEnv*&env,&char*&str&) &{ &&&&&jstring&rtn&=&0; &&&&&int&slen&=&strlen(str); &&&&&unsigned&short&*&buffer&=&0; &&&&&if(&slen&==&0&) &&&&&&&&&rtn&=&(env)-&NewStringUTF(str&);& &&&&&else&&&&&{ &&&&&&&&&int&length&=&MultiByteToWideChar(&CP_ACP,&0,&(LPCSTR)str,&slen,&NULL,&0&); &&&&&&&&&buffer&=&(unsigned&short&*)malloc(&length*2&+&1&); &&&&&&&&&if(&MultiByteToWideChar(&CP_ACP,&0,&(LPCSTR)str,&slen,&(LPWSTR)buffer,&length&)&&0&) &&&&&&&&&&&&&rtn&=&(env)-&NewString(&&(jchar*)buffer,&length&); &&&&&} &&&&&if(&buffer&) &&&&&&&&&free(&buffer&); &&&&&return& &}&
根据前面的字符流的分析,和后面提供的转换方法,基本上可以解决jni中中文参数乱码的问题。
以上内容部分摘自网络。属于个人总结内容。
原文链接:
【编辑推荐】
【责任编辑: TEL:(010)】
关于&&的更多文章
Java既是一种高级的面向对象的编程语言,也是一个平台。Java平台
作为移动开发者,WOT2016移动互联网技术峰会,绝对有你不得不来的理由。
在过去的近十年的时间里,面向对象编程大行其道。以至
很久很久以前,冬天爱上了夏天,可是他们始终不能相见
流火过,金秋来,在学校开学之前的最后一周,终于凉快
本书详细描述如何在复杂技术项目中使用Scrum,并结合真实的Scrum案例及专家洞识,在简明及高度概括的理论之上更侧重于实践,并不
51CTO旗下网站}

我要回帖

更多关于 jni返回字符串数组 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信