A. JNI编程之如何传递参数(一)——String参数的传递
先看一个例子,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("Prompt"); } }在这个例子中,我们要实现一个native方法 String getLine(String prompt); 读入一个String参数,返回一个String值。通过执行javah -jni得到的头文件是这样的#include<jni.h> #ifndef_Included_Prompt #define_Included_Prompt #ifdef __cplusplus extern"C" { #endif JNIEXPORT jstringJNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt); #ifdef __cplusplus } #endif #endifjstring是JNI中对应于String的类型,但是和基本类型不同的是,jstring不能直接当作C++的string用。如果你用 cout << prompt << endl; 编译器肯定会扔给你一个错误信息的。其实要处理jstring有很多种方式,这里只讲一种我认为最简单的方式,看下面这个例子,#include"Prompt.h" #include<iostream> JNIEXPORT jstringJNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) { const char* str; str = env->GetStringUTFChars(prompt, false); if(str == NULL) { return NULL; /* OutOfMemoryError already thrown */ } std::cout << str << std::endl; env->ReleaseStringUTFChars(prompt, str); char* tmpstr = "return string succeeded"; jstring rtstr = env->NewStringUTF(tmpstr); return rtstr; }在上面的例子中,作为参数的prompt不能直接被C++程序使用,先做了如下转换 str = env->GetStringUTFChars(prompt, false); 将jstring类型变成一个char*类型。返回的时候,要生成一个jstring类型的对象,也必须通过如下命令,
B. jni问题:返回NewStringUTF的jstring 是由jvm管理还是需要手动释放
有个需求,需要jni中返回jstring,由于一直在c++中使用string拼接字符串,所以返回得把string转成jstring,网上搜了下,非常麻烦,再加上我返回jstring用的统一接口是
env->NewStringUTF(result)
env为jni指针,result为const char*
所以想到先把string转成const char*,然后直接调用这个接口返回string,代码如下:
string str = "";
result = str.c_str();
return env->NewStringUTF(result);
如此编译so运行之后确实起初没发现没问题,本来也以为此方案OK,但是在红米note2上发现返回乱码,分析得知str.c_str()返回的指针会由于str的被回收而导致指向垃圾内容。如此一来转换解决方案如下:
char* c;
const int len = str.length();
c =new char[len+1];
strcpy(c,str.c_str());
const char* result = c;
return result;
如此将str里的内容赋值给一个const char*即可。
C. 在JNI中如何将jobject类型转换为jdouble类型
#include<jni.h>
#include"com_test_Test.h"
#include<stdio.h>
#include<stdlib.h>
#include<strings.h>
//获取字符串
JNIEXPORTvoidJNICALLJava_com_test_Test_sayHello(JNIEnv*env,jobjectobj,
jstrings){
char*str=(char*)(*env)->GetStringUTFChars(env,s,0);
printf(" c-string:hello-%s",str);
}
//获取,返回int
JNIEXPORTjintJNICALLJava_com_test_Test_add(JNIEnv*env,jobjectobj,
jinta,jintb){
inti=a;
intj=b;
printf(" c-int:%d-%d",i,j);
charstr[256];
sprintf(str,"%d",i+j);
return(jint)i+j;
}
//获取,返回float
JNIEXPORTjfloatJNICALLJava_com_test_Test_getFloat(JNIEnv*env,jobjectobj,
jfloatf){
floatfl=f;
printf(" c-float:%3f",fl);
fl=200.555;
return(jfloat)fl;
}
//获取,返回double
JNIEXPORTjdoubleJNICALLJava_com_test_Test_getDouble(JNIEnv*env,
jobjectobj,jdoubledou){
doubled=dou;
printf(" c-double:%3f",d);
d=800.88;
return(jdouble)d;
}
//获取,返回boolean
JNIEXPORTjbooleanJNICALLJava_com_test_Test_getBoolean(JNIEnv*env,
jobjectobj,jbooleanbool){
unsignedcharb=bool;
printf(" c-boolean:%lu",b);
if(b){
printf("true");
}else{
printf("false");
}
b=1;
return(jboolean)b;
}
//获取,返回string
JNIEXPORTjstringJNICALLJava_com_test_Test_getString(JNIEnv*env,
jobjectobj,jstrings){
char*st=(char*)(*env)->GetStringUTFChars(env,s,0);
printf(" c-string:%s",st);
char*str="hellowangwu!";
jstringrtn;
rtn=(*env)->NewStringUTF(env,str);
returnrtn;
}
//获取,返回string[]
_com_test_Test_getStringArray(JNIEnv*env,
jobjectobj,jobjectArrayarr){
intlen=(*env)->GetArrayLength(env,arr);
printf(" c-stringArray:");
inti=0;
for(i=0;i<len;i++){
jobjectobj=(*env)->GetObjectArrayElement(env,arr,i);
jstringstr=(jstring)obj;
constchar*szStr=(*env)->GetStringUTFChars(env,str,0);
printf("%d-%s",i,szStr);
(*env)->ReleaseStringChars(env,str,szStr);
}
//-----返回----
jstringstr;
jobjectArrayargs=0;
jsizesize=5;
char*sa[]={"Hello,","world!","zhang","san","yuang"};
intj=0;
jclassobjClass=(*env)->FindClass(env,"java/lang/String");
args=(*env)->NewObjectArray(env,size,objClass,0);
for(j=0;j<size;j++){
str=(*env)->NewStringUTF(env,sa[j]);
(*env)->SetObjectArrayElement(env,args,j,str);
}
returnargs;
}
-----------------------------------------------------------------
下面是Test.java
packagecom.test;
publicclassTest{
privatenativevoidsayHello(Stringstr);
privatenativeintadd(inta,intb);
privatenativefloatgetFloat(floatf);
privatenativedoublegetDouble(doubled);
(booleanb);
privatenativeStringgetString(Stringstr);
privatenativeString[]getStringArray(String[]sa);
static{
System.loadLibrary("Test");
}
publicstaticvoidmain(String[]args){
Testtest=newTest();
test.sayHello("zhangsan");
System.out.println("int-->"+test.add(10,20));
System.out.println("float-->"+test.getFloat((float)20.123));
System.out.println("double-->"+test.getDouble(100.369));
System.out.println("boolean-->"+test.getBoolean(true));
System.out.println("string-->"+test.getString("wangWu"));
String[]ss={"hello","-","zhang","san"};
Object[]obj=test.getStringArray(ss);
System.out.print("string[]-->");
for(Objectobject:obj){
System.out.print(object+"");
}
}
}
D. c++ 中怎么讲jstring 转换为 String(java中的类型)
他们俩是一个类型只不过一个是封装类而已就如同 int 和interger
E. C++ 数字转字符串 double 转换成字符串 就是char类型的 或者是 jstring也行
可以有好几种方法,比如dbl为那个double变量:
c的方法:
char buffer[32];
snprintf(buffer, 32, "%g", dbl);
boost方法:
std::string str = boost::lexical_cast<std::string>(dbl);
stringstream方法:
std::ostringstream strs;
strs << dbl;
std::string str = strs.str();
F. 怎么把jstring转换成char
//jstring to char*
char* jstringTostring(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}
//char* to jstring
jstring stoJstring(JNIEnv* env, const char* pat)
{
jclass strClass = env->FindClass("Ljava/lang/String;");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = env->NewStringUTF("utf-8");
return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}
G. 将const char类型转换成jstring类型中的 NewStringUTF("GB2312");是何含义
jstring
encoding
=
(env)->NewStringUTF("GB2312");
这句只是得到一个内容为“GB2312”的jstring字符串而已,因为这个字符串后面NewObject创建jstring时,jstring的构造函数需要用到(想想你在JAVA里将byte数组转换为一个GB2312编码的String会怎么做,你会需要在构造函数那明确写一个“GB2312“这样的参数)。
总而言之,你这里给出的所有代码的目的就是:将一个C语言中的char*字符串转换为编码为GB2312的JAVA字符串(在JNI中用jstring代表)并返回。
H. 如何解决jni char转化为jstring乱码问题
java内部是使用16bit的unicode编码(UTF-16)来表示字符串的,无论中文英文都是2字节; jni内部是使用UTF-8编码来表示字符串的,UTF-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节; c/c++使用的是原始数据,ascii就是一个字节了,中文一般是GB2312编码,用两个字节来表示一个汉字。
明确了概念,操作就比较清楚了。下面根据字符流的方向来分别说明一下
1、java --> c/c++
这种情况中,java调用的时候使用的是UTF-16编码的字符串,jvm把这个字符串传给jni,c/c++得到的输入是jstring,这个时候,可以利用jni提供的两种函数,一个是GetStringUTFChars,这个函数将得到一个UTF-8编码的字符串;另一个是 GetStringChars这个将得到UTF-16编码的字符串。无论那个函数,得到的字符串如果含有中文,都需要进一步转化成GB2312的编码。
2、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。
这里要说明一下:Linux和win32都支持wchar,这个事实上就是宽度为16bit的unicode编码UTF16,所以,如果我们的 c/c++程序中完全使用wchar类型,那么理论上是不需要这种转换的。但是实际上,我们不可能完全用wchar来取代char的,所以就目前大多数应用而言,转换仍然是必须的。
二、一种转换方法
使用wide char类型来转换。
char* jstringToWindows( JNIEnv *env, jstring jstr )
{ //UTF8/16转换成gb2312
int length = (env)->GetStringLength(jstr );
const jchar* jcstr = (env)->GetStringChars(jstr, 0 );
char* rtn = (char*)malloc( length*2+1 );
int size = 0;
size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL );
if( size <= 0 )
return NULL;
(env)->ReleaseStringChars(jstr, jcstr );
rtn[size] = 0;
return rtn;
}
jstring WindowsTojstring( JNIEnv* env, const char* str )
{//gb2312转换成utf8/16
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 rtn;
}