在NDK下做网络传输时,遇到一个编码转换的问题,就是对方传过来的文件名是中文GBK编码,需要转成UTF8来处理。
平常在C/C++环境下编程时,系统都会提供字符编码转换的API。如Windows下有MultiByteToWideChar之类的函数,Linux下有iconv库,纯C下用wcstombs、mbstowcs也可以做。然而我在我机上的Android NDK目录下,并没有找到iconv库,直接写iconv函数无法编译;而mbstowcs需要的本地库似乎也没有,即使能编译也会运行失败。这一来,似乎就没有办法,只能自己手工写码表库转换了。
后来还是想到一个办法:由于Android下NDK是由Java JNI调用的,而Java本身的字符编码转换功能是现成的,直接new一个String,传入字节和编码,就可以获取指定另一个编码的字符了。
通过网上查资料试验,终于编译运行通过,问题解决。Java这边调JNI的代码就不贴了,C程序大概内容及说明如下:
//首先是包含头文件,并定义全局变量
#include <string.h>
#include <jni.h>
#include <pthread.h>
//初始JNI虚拟机环境和线程
JavaVM* gJavaVM;
JNIEnv* gJniEnv;
pthread_t gJvmThread;
//Java字符串的类和获取字节的方法ID
jclass gStringClass;
jmethodID gmidStringInit;
jmethodID gmidStringGetBytes;
//初始化JNI环境,此函数由JNI调用
jstring Java_com_huz_test_CharsetTest_InitJNIEnv(JNIEnv* env, jobject obj)
{
(*env)->GetJavaVM(env, &gJavaVM);
gJniEnv=env;
gJvmThread=pthread_self();//记住当前JNI环境的线程
//获取Java String类和回调方法ID信息,由于每次转换都需要,因此用全局变量记下来,免得浪费时间重复执行
gStringClass= (*env)->FindClass(env,"java/lang/String");
gmidStringGetBytes= (*env)->GetMethodID(env,gStringClass, "getBytes", "(Ljava/lang/String;)[B");
gmidStringInit= (*env)->GetMethodID(env,gStringClass, "<init>", "([BLjava/lang/String;)V");
...
return (*env)->NewStringUTF(env, "OK");
}
//由Java String转为指定编码的char
int jstringToPchar(JNIEnv* env, jstring jstr, const char * encoding, char* outbuf, int outlen)
{
char* rtn = NULL;
jstring jencoding;
if (encoding==HNULL)
jencoding= (*env)->NewStringUTF(env,"utf-8");
else
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)
{
if(outlen==0)
return alen;
if(outlen<=alen)
return -1;
rtn=outbuf;
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env,barr, ba, 0);
return alen;
}
//由指定编码以零结束的char转为Java String
jstring pcharToJstring(JNIEnv* env, const char* pat, const char* encoding)
{
jstring jencoding;
jbyteArray bytes = (*env)->NewByteArray(env,strlen(pat));
(*env)->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte*)pat);
if (encoding==HNULL)
jencoding= (*env)->NewStringUTF(env,"utf-8");
else
jencoding=(*env)->NewStringUTF(env,encoding);
return (jstring)(*env)->NewObject(env,gStringClass, gmidStringInit, bytes, jencoding);
}
//在C代码中执行字符串编码转换
//参数分别为:原始字符,原始编码,目标字符缓冲区,目标编码,目标缓冲区大小,返加转换结果的长度
int changeCharset(char * src_buf, char * src_encoding, char * dst_buf, char * dst_encoding, int dst_size)
{
JNIEnv *env;
jstring jtemp;
int res;
//由于初始化只执行了一次,本函数与初始JNI调用可能不在同一线程,因此需要判断当前线程
if(gJvmThread==pthread_self())
{
//如果是同一个线程,直接转
env=gJniEnv;
jtemp=pcharToJstring(env, src_buf, src_encoding);
res=jstringToPchar(env, jtemp,dst_encoding, dst_buf, dst_size);
}
else
{
//如果不是同一个线程,先Attach再转
env=gJniEnv;
(*gJavaVM)->AttachCurrentThread(gJavaVM,&env,NULL);
jtemp=pcharToJstring(env, src_buf, src_encoding);
res=jstringToPchar(env, jtemp,dst_encoding, dst_buf, dst_size);
(*gJavaVM)->DetachCurrentThread(gJavaVM);
}
return res;
}
虽然JNI回调能解决此问题,但用起来很麻烦,速度上估计也很慢,适合少量的数据处理。估计以后Android的NDK新版应该会解决此问题。
以上转自:http://www.linuxidc.com/Linux/2011-02/32354.htm
注解:$ whereis iconv
iconv: /bin/iconv.exe /usr/bin/iconv.exe /usr/include/iconv.h /usr/share/man/man1/iconv.1.gz /usr/share/man/man3/iconv.3.gz
明明存在,但是#include<iconv.h>,报错信息【error: iconv.h: No such file or directory】。
这个问题初步估计可以通过修改Android.mk文件配置LOCAL_C_INCLUDES可以解决。正在研究中,大家谁有这方面经验,欢迎留言。
分享到:
相关推荐
Android NDK调用C/C++ 简单样例,适合初次搭建ndk平台的人参考
android studio中使用ndk编译.so文件,调用C/C++代码(jni编程)
android NDK入门篇----混合使用java和c/c++代码 源码工程 包括头文件 源文件以及生成的so库
Android JNI/NDK开发(2)JNI实现C/C++与Android/JAVA相互调用 http://blog.csdn.net/u014702653/article/details/71141423
安卓app开发之NDK入门教程,JAVA代码通过JNI接口调用NDK代码(C语言编写的linux android功能).zip
Android调用C/C++(NDK),压缩包内含代码及说明文档(下载NDK和构建工具、构建及运行第一个NDK项目,有图片,有注解,手把手教会)
JNI开发Java和C/C++互相传递List集合, 可以参考: Java从C/C++获取List集合对象:https://blog.csdn.net/niuba123456/article/details/80994166 Java传递List集合对象到C/C++ ...
Android NDK 开发时需要的 常用的JNI API
Android Studio JNI/NDK 编程; 具体见博客: http://blog.csdn.net/q610098308/article/details/51313341
基于Opensl接口实现的录音demo,代码经过测试验证,功能正常,使用循环BUFFER实现录音和写入文件功能。需要的朋友可以参考使用
Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例Android NDK JNI 经典实例
Android-ndk-jni AES加解密 压缩文件里面有 aes加解密 .c .h 文件 还用使用方法
附件是Android下检测ndk和jni内存泄漏的demo,可以用于native中malloc和free的检测。使用方法(参见博客):https://blog.csdn.net/zhuyong006/article/details/88537499
Android Studio中使用NDK/JNI 的相关代码http://blog.csdn.net/jfzl123/article/details/53436600
本书顺应Android软/硬件、云计算整合潮流,详细剖析了NDK开发中涉及的各类问题和解决方案:搭建Android NDK开发环境的每一步细节,开发第一个Android NDK程序,Android NDK中Java与C/C++代码的互相调用,Facade设计...
Android C++高级编程 使用NDK,Android C++高级编程 使用NDK
主要内容:● 使用JNI将原生代码连接到Java中● 使用SWIG自动生成JNI代码● 使用POSIX线程实现多线程应用● 使用POSIX套接字实现网络应用● 使用logging、GDB和Eclipse调试器进行调试● 使用Valgrind分析内存● 使用...
Android-NDK-MD5-安卓jni 进行md5加密; Android-NDK-MD5-安卓jni 进行md5加密
配置项目包含: 1. 配置JDK和Android SDK以及Android NDK的环境变量; 2. eclipse生成生成项目中所有头文件:...6. 将NDK中C/C++库文件路径加入到项目组(去除编译错误和警告提示,包括NDK的API和C/C++库):
Android JNI 断点调试C++,一个简单测试,方便初学者入门NDK环境搭建和java调用c++并调试