以前在使用jni中,发现jni字符问题,另外jni还存在着线程的不安全性,着实感觉jni不怎么地,但是目前没有好的jni的替代方案,这次介绍的是jni字符编码问题中需要注意的一个问题。
一般我们使用的是utf8字符编码,在确认是utf8编码的同时,我们从java字串传递到c/c++库的字串是有个一个标准的接收过程,这个过程我们暂且称之为:编码传递
编码传递是将jstring类型转换为char *或者const char*类型,其中在转换的过程中,需要有对字符编码的处理:
JNIEXPORT void JNICALL Java_com_alibaba_judystore_JudyStoreClient_modify(JNIEnv *env, jobject obj, jstring string, jboolean isOffer)上面使用了jni内置的GetStringUTFChars来接收jstring,然后返回一个const char*类型,值得注意的是,这个这个指针分配的内存需要以ReleaseStringUTFChars方式释放,而不是delete和free。
{
const char *key = env->GetStringUTFChars(string, 0);
jsClient->modify(key, isOffer);
env->ReleaseStringUTFChars(string, key);
}
另外,有一种jni的方式是读取本地的字符串,见文件:jniutils.h,其中:
char *JNU_GetStringNativeChars(JNIEnv *env, jstring jstr)然后使用JNU_GetStringNativeChars来调用:
{
jbyteArray bytes = 0;
char *result = 0;
jthrowable exc;
if (env->EnsureLocalCapacity(2) < 0)
{
return 0; /* out of memory error */
}
if (!MID_String_getBytes)
MID_String_getBytes = env->GetMethodID(env->FindClass(
"java/lang/String"), "getBytes", "()[B");
bytes = (jbyteArray) env->CallObjectMethod(jstr, MID_String_getBytes);
exc = env->ExceptionOccurred();
if (!exc)
{
jint len = env->GetArrayLength(bytes);
result = new char[len + 1];
if (result == 0)
{
JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0);
env->DeleteLocalRef(bytes);
return 0;
}
env->GetByteArrayRegion(bytes, 0, len, (jbyte *) result);
result[len] = 0; /* NULL-terminate */
}
else
{
env->DeleteLocalRef(exc);
}
env->DeleteLocalRef(bytes);
return result;
}
JNIEXPORT jstring JNICALL Java_com_alibaba_offer_OfferClassifierJNI_nativeClassify这样,会造成如果当前环境(locale)不是UTF-8,会造成编码传递失效,导致不可预料的错误。
(JNIEnv *env, jobject, jstring jstr)
{
char *cstr = JNU_GetStringNativeChars(env, jstr);
string str = string(cstr);
double score;
string category = classifier->classify(str, score);
delete cstr;
return env->NewStringUTF(category.c_str());
}