JNI的字符串字符集转换

以下方式 总归 还是在使用使用java的string:java/lang/String

现在,你知道了如何通过JNI来访问JVM中的基本类型数据和字符串、数组这样的引用类型数据,下一步就是学习怎么样和JVM中任意对象的字段和方法进行交互。比如从本地代码中调用JAVA中的方法,也就是通常说的来自本地方法中的callbacks(回调)。
我们从进行字段访问和方法回调时需要的JNI函数开始讲解。本章的稍后部分我们会讨论怎么样通过一些cache(缓存)技术来优化这些操作。在最后,我们还会讨论从本地代码中访问字段和回调方法时的效率问题。

治根的办法 还是完整地在native code 中实现。推荐可以使用开源代码icu
或者iconv

4.1 访问字段

android本身也是用了开源的库:iconv,只默认是未对外导出。

JAVA支持两种field(字段),每一个对象的实例都有一个对象字段的复制;所有的对象共享一个类的静态字段。本地方法使用JNI提供的函数可以获取和修改这两种字段。先看一个从本地代码中访问对象字段的例子:
class InstanceFieldAccess {
private String s;

先放段代码作为开头,本段主要调用java中到构造函数,本段代码实现,字符串字符集转换。

 private native void accessField();
 public static void main(String args[]) {
     InstanceFieldAccess c = new InstanceFieldAccess();
     c.s = "abc";
     c.accessField();
     System.out.println("In Java:");
     System.out.println("  c.s = "" + c.s + """);
 }
 static {
     System.loadLibrary("InstanceFieldAccess");
 }
jstringMyNewString(JNIEnv*env,constchar*chars,jintlen){jclassstringClass;jmethodIDcid;jbyteArrayelemArr;jstringresult;jstringjencoding;stringClass=env-FindClass(java/lang/String);if(stringClass==NULL){returnNULL;/*exceptionthrown*/}/*GetthemethodIDfortheString(byte[]data,StringcharsetName)constructor*/cid=env-GetMethodID(stringClass,init,([BLjava/lang/String;)V);if(cid==NULL){returnNULL;/*exceptionthrown*/}jencoding=env-NewStringUTF(GBK);/*Createabyte[]thatholdsthestringcharacters*/elemArr=env-NewByteArray(len);if(elemArr==NULL){returnNULL;/*exceptionthrown*/}env-SetByteArrayRegion(elemArr,0,len,(jbyte*)chars);/*Constructajava.lang.Stringobject*/result=(jstring)(env-NewObject(stringClass,cid,elemArr,jencoding));/*Freelocalreferences*/env-DeleteLocalRef(elemArr);env-DeleteLocalRef(stringClass);returnresult;}

}
InstanceFieldAccess这个类定义了一个对象字段s。main方法创建了一个对象并设置s的值,然后调用本地方法InstanceFieldAccess.accessField在本地代码中打印s的值,并把它修改为一个新值。本地方法返回后,JAVA中把这个值再打印一次,可以看出来,字段s的值已经被改变了。下面是本地方法的实现:
JNIEXPORT void JNICALL
Java_InstanceFieldAccess_accessField(JNIEnv env, jobject obj)
{
jfieldID fid; /
store the field ID */
jstring jstr;
const char *str;

以上代码时是参照《The JavaTM NativeInterfaceProgrammer’s Guide and
Specification》改的。

 /* Get a reference to obj's class */
 jclass cls = (*env)->GetObjectClass(env, obj);

 printf("In C:n");

 /* Look for the instance field s in cls */
 fid = (*env)->GetFieldID(env, cls, "s",
                          "Ljava/lang/String;");
 if (fid == NULL) {
     return; /* failed to find the field */
 }




 /* Read the instance field s */
 jstr = (*env)->GetObjectField(env, obj, fid);
 str = (*env)->GetStringUTFChars(env, jstr, NULL);
 if (str == NULL) {
     return; /* out of memory */
 }
 printf("  c.s = "%s"n", str);
 (*env)->ReleaseStringUTFChars(env, jstr, str);

 /* Create a new string and overwrite the instance field */
 jstr = (*env)->NewStringUTF(env, "123");
 if (jstr == NULL) {
     return; /* out of memory */
 }
 (*env)->SetObjectField(env, obj, fid, jstr);

这里将字符集转换的全部代码贴上,记下以备忘,此外如果对大家有帮助,那就更好了。

}
运行程序,得到输出为:
In C:
c.s = “abc”
In Java:
c.s = “123”

说穿了,其实就是在调用java.lang.String的方法。

4.1.1 访问一个对象字段的流程

一、CharToJString。

为了访问一个对象的实例字段,本地方法需要做两步:
首先,通过在类引用上调用GetFieldID获取field
ID(字段ID)、字段名字和字段描述符:
Fid=(env)->GetFieldID(env,cls,”s”,”Ljava/lang/String;”);
上例中的代码通过在对象引用obj上调用GetObjectClass获取到类引用。一旦获取到字段ID,你就可以把对象和字段ID作为参数来访问字段:
Jstr=(
env)->GetObjectField(env,obj,fid);
因为字符串和数组是特殊的对象,所以我们使用GetObjectField来访问字符串类型的实例字段。除了Get/SetObjectField,JNI还支持其它如GetIntField、SetFloatField等用来访问基本类型字段的函数。

二、JStringToChar。

4.1.2 字段描述符

Mark:

在上一节我们使用过一个特殊的C字符串“Ljava/lang/String”来代表一个JVM中的字段类型。这个字符串被称为JNI
field descriptor(字段描述符)。
字符串的内容由字段被声明的类型决定。例如,使用“I”来表示一个int类型的字段,“F”来表示一个float类型的字段,“D”来表示一个double类型的字段,“Z”来表示一个boolean类型的字段等等。
像java.lang.String这样的引用类型的描述符都是以L开头,后面跟着一个JNI类描述符,以分号结尾。一个JAVA类的全名中的包名分隔符“.”被转化成“/”。因此,对于一个字段类型的字段来说,它的描述符是“Ljava/lang/String”。
数组的描述符中包含“]”字符,后面会跟着数组类型的描述符,如“[I”是int[]类型的字段的描述符。12.3.3详细介绍了各种类型的字段描述以及他们代表的JAVA类型。
你可以使用javap工具来生成字段描述符。

以下代码在转中英文混合的字符串时会出错,原因是参数中的char*。更合适的接口可能应该如下:

4.1.3 访问静态字段

jstringCharToJString(JNIEnv*env,constvoid*pszSrc,intnLen,constchar*pszEncoding);intJStringToChar(JNIEnv*env,jstringjstr,constchar*pszEncoding,void*pszOutBuf,intnBufLen);

访问静态字段和访问实例字段相似,看下面这个InstanceFieldAccess例子的变形:
class StaticFielcdAccess {
private static int si;

java/native调用会比java/java调用慢上2~3倍,而虽然native/java调用,理论上和java/native效率一样,可是,实际上,由于后者的使用比前者多得多,所以java/native可能得到了足够多优化,这样,native/java调用可能比java/native调用慢上10倍之多。

 private native void accessField();
 public static void main(String args[]) {
     StaticFieldAccess c = new StaticFieldAccess();
     StaticFieldAccess.si = 100;
     c.accessField();
     System.out.println("In Java:");
     System.out.println("  StaticFieldAccess.si = " + si);
 }
 static {
     System.loadLibrary("StaticFieldAccess");
 }
P/PPREclass=cppname=code/**youcanusethesefunctionfreely,butpleasekeepthisdeclarationwithnomodified.**createdbyjerry.lin,2011-08-04*/#includestdio.h#includejni.h#includestring.h/**convertthecharstringtoJString*parameter:*[in]pszSrc:thesourcestringtobeconverted.*[in]pszEncoding:theencodingofpszSrc,ifitisnull,meansutf8**returnvalue:*returntheconveredstringifissuccessful,otherwisereturnNULL.*/jstringCharToJString(JNIEnv*env,constchar*pszSrc,constchar*pszEncoding){jstringjstr=NULL;jclassstringClass=NULL;jbyteArraybyteElemArr=NULL;do{jmethodIDcid=NULL;jstringjstrEncoding=NULL;constchar*pszEnco=NULL;intnStrLen=0;/*checktheincomeparameters*/if(NULL==pszSrc||NULL==env){break;}/*gettheStringclassofjavaanditsconstructortocreateanewobject*//*gettheStringclassofjava*/stringClass=env-FindClass(java/lang/String);if(NULL==stringClass){break;}/*GetthemethodIDforthejava.lang.Stringconstructor*/cid=env-GetMethodID(stringClass,init,([BLjava/lang/String;)V);if(NULL==cid){break;}if(NULL==pszEncoding){pszEnco=utf-8;}else{pszEnco=pszEncoding;}jstrEncoding=env-NewStringUTF(pszEnco);if(NULL==jstrEncoding){break;}/*putcharstringintoarrayofjava*/nStrLen=(int)strlen(pszSrc);byteElemArr=env-NewByteArray(nStrLen);if(NULL==byteElemArr){break;}env-SetByteArrayRegion(byteElemArr,0,nStrLen,(jbyte*)pszSrc);/*createannew0objectofjava.lang.Stringwiththeconstructorstring(byte[],string)*/jstr=(jstring)(env-NewObject(stringClass,cid,byteElemArr,jstrEncoding));}while(0);/*Freelocalreferences*/if(NULL!=byteElemArr){env-DeleteLocalRef(byteElemArr);}if(NULL!=stringClass){env-DeleteLocalRef(stringClass);}returnjstr;}/**converttheJStringtocharstring.**parameter:*[in]jstr:thesourcestringtobeconverted.*[in]pszEncoding:theencodingtowhichtheJStringtobeconverted.*[out]pszOutBuf:thebuffertoremainthechangedstring.*[in]nBufLen:thelengthofthebuffer.**returnvalue:*thelengthofthestringinbytes,thathasbeencopiedintopszOutBuf.*/intJStringToChar(JNIEnv*env,jstringjstr,constchar*pszEncoding,char*pszOutBuf,intnBufLen){intnRet=-1;jclassstringClass=NULL;jbyteArraybyteElemArr=NULL;do{jmethodIDcid=NULL;jstringjstrEncoding=NULL;constchar*pszEnco=NULL;intnStrLen=0;/*checktheincomeparameters*/if(NULL==jstr||NULL==env){break;}/*gettheStringclassofjava*/stringClass=env-FindClass(java/lang/String);if(NULL==stringClass){break;}/*GetthemethodIDforthejava.lang.StringgetBytes(String)*/cid=env-GetMethodID(stringClass,getBytes,(java/lang/String;)[B);if(NULL==cid){break;}if(NULL==pszEncoding){pszEnco=utf-8;}else{pszEnco=pszEncoding;}jstrEncoding=env-NewStringUTF(pszEnco);if(NULL==jstrEncoding){break;}/*getcharstringintoarraywithdesignatedencode*/byteElemArr=(jbyteArray)env-CallObjectMethod(jstr,cid,jstrEncoding);if(NULL==byteElemArr){break;}nStrLen=(int)(env-GetArrayLength(byteElemArr));/*toreturnthelengthofthecharstring,ifnBufLenis0,orpszOutBufisNULL*/if(0==nBufLen||NULL==pszOutBuf){nRet=nStrLen;break;}/*ifthenBufLennStrLen,returnfailed*/if(nBufLennStrLen){nRet=-2;break;}/*getthecontentofthebyteArray,andcopyintotheoutbuffer*/jbyte*pszBytes=env-GetByteArrayElements(byteElemArr,JNI_FALSE);if(NULL!=pszBytes){memcpy(pszOutBuf,pszBytes,nStrLen);pszOutBuf[nStrLen]=0;nRet=nStrLen;}}while(0);/*Freelocalreferences*/if(NULL!=byteElemArr){env-DeleteLocalRef(byteElemArr);}if(NULL!=stringClass){env-DeleteLocalRef(stringClass);}returnnRet;}/PREBRBRP/PPBR/PPRE/PREPRE/PRE

}
StaticFieldAccess这个类包含一个静态字段si,main方法创建了一个对象,初始化静态字段,然后调用本地方法StaticFieldAccess.accessField在本地代码中打印静态字段中的值,然后设置新的值,为了演示这个值确实被改变了,在本地方法返回后,JAVA中再次这个静态字段的值。
下面是本地方法StaticFieldAccess.accessField的实现:
JNIEXPORT void JNICALL
Java_StaticFieldAccess_accessField(JNIEnv env, jobject obj)
{
jfieldID fid; /
store the field ID */
jint si;

 /* Get a reference to obj's class */
 jclass cls = (*env)->GetObjectClass(env, obj);

 printf("In C:n");

 /* Look for the static field si in cls */
 fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
 if (fid == NULL) {
     return; /* field not found */
 }
 /* Access the static field si */
 si = (*env)->GetStaticIntField(env, cls, fid);
 printf("  StaticFieldAccess.si = %dn", si);
 (*env)->SetStaticIntField(env, cls, fid, 200);

澳门新葡亰3522平台游戏,}
运行程序可得到输出结果:
In C:
StaticFieldAccess.si = 100
In Java:
StaticFieldAccess.si = 200
访问静态字段和对象实例字段的不同点:
1、
访问静态字段使用GetStaticFieldID,而访问对象的实例字段使用GetFieldID,但是,这两个方法都有相同的返回值类型:jfieldID。

4.2 调用方法