I have a JNI function that returns a UChar array (from the ICU4C library) which I'd like to convert to a Java character array so I can call this from Java. I am not sure where the problem is as whenever I access this JNI function, everything crashes and hangs but I get no error message anywhere, including in the logcat... very difficult to debug!
Can the UChar array map directly to a jcharArray type? also, can I use it as a return type? or could I pass it in as a parameter that the JNI function then populates?
Here is a snippet of basically what I am trying to do:
static jint testFunction(JNIEnv* env, jclass c, jobject obj, jcharArr开发者_StackOverflow社区ay chsArray,
int offset, int len, jcharArray dstArray) {
jchar* dst = env->GetCharArrayElements(dstArray, NULL);
if (dst != NULL) {
UChar *str = new UChar[len];
//populate str here from an ICU4C function
for (int i=0; i<len; i++)
dst[i] = str[i]; //this might be the problematic piece of code (can I issue an assignment like this?)
}
}
env->ReleaseCharArrayElements(dstArray, dst, 0);
}
Any help is appreciated!
Thanks
JNI can be a real headache. You function looks fine, on the surface.
First, I note that you are not using offset
- that's a code-smell.
Second, you are not freeing the UChar array.
Third, either the C function or the assignment loop may be overrunning the array bounds.
To assist with locating abrupt crashes like this, I have successfully used a good-ol-fashioned print
statement in conjunction with the console.
First I added a println method to my JNIGlobal class:
/** Print text or ASCII byte array prefixed with "JNI: ". Primarily for native code to output to the Java console. */
static public void println(Object val) {
if(val instanceof byte[]) { byte[] ba=(byte[])val; val=new String(ba,0,ba.length); }
System.out.println("JNI: "+val);
}
Then I added a corresponding method to my C code:
void println(JNIEnv *jep, byte *format,...) {
va_list vap;
byte txt[5001];
jsize txtlen;
jclass eCls;
jint mId;
jbyteArray jText;
va_start(vap,format); vsprintf(txt,format,vap); va_end(vap);
txtlen=(long)strlen(txt);
if((eCls=(*jep)->FindClass(jep,"<your/package/here/JNIGlobal"))==0) {
printf("JNI: Global class not found (Error Text: %s)\n",txt);
return; /* give up */
}
if((mId=(*jep)->GetStaticMethodID(jep,eCls,"println","(Ljava/lang/Object;)V"))==0) {
printf("JNI: Global println method not found (Error Text: %s)\n",txt);
return; /* give up */
}
jText=(*jep)->NewByteArray(jep,txtlen);
(*jep)->SetByteArrayRegion(jep,jText,0,txtlen,(void*)txt);
(*jep)->CallStaticVoidMethod(jep,eCls,mId,jText);
}
Then I just call println(env,"Some formatted output")
at each line in the source to see how far it gets. In my environment (an AS/400), when the JVM crashes during interative running I am left with the console - you might want to add a short delay in the Java code to ensure you see the output before your console goes away.
So for you, like so:
static jint testFunction(JNIEnv* env, jclass c, jobject obj,
jcharArray chsArray, int offset, int len, jcharArray dstArray) {
/**/println("** testFunction 1");
jchar* dst = env->GetCharArrayElements(dstArray, NULL);
/**/println("** testFunction 2");
if (dst != NULL) {
/**/println("** testFunction 3");
UChar *str = new UChar[len];
/**/println("** testFunction 4");
//populate str here from an ICU4C function
/**/println("** testFunction 5");
for (int i=0; i<len; i++)
dst[i] = str[i]; //this might be the problematic piece of code (can I issue an assignment like this?)
}
/**/println("** testFunction 6");
}
env->ReleaseCharArrayElements(dstArray, dst, 0);
/**/println("** testFunction 7");
}
If your intent is to get a UChar* value from ICU and return a string to Java (I'm assuming this based on the "populate str here from ICU4C function" comment), why not just use a jstring?
For example:
jstring Java_com_mysomethingsomething_test_getAString(JNIEnv* env, jobject thiz)
{
UChar* buf = new UChar[BUF_LEN];
int32_t len;
PouplateBuffer(buf, &len); //populate str here from an ICU4C function
jstring result = env->NewString(reinterpret_cast<jchar*>(buf), static_cast<jint>(len));
delete [] buf;
return result;
}
The example is simplifed of course, but should illustrate UChar* to jstring conversion. This also easily works with a UnicodeString:
jstring Java_com_mysomethingsomething_test_getAString(JNIEnv* env, jobject thiz)
{
const UnicodeString result = PopulateString();
return env->NewString(reinterpret_cast<jchar*>(result.getBuffer()), static_cast<jint>(result.length()));
}
How long is the dstArray? c++ cannot check array bounds and happily corrupts the memory of your process if len is larger than dstArray.length.
ICU4JNI is not actively maintained, but you might be able to look at it for an example of calling ICU4C from JNI. See also ICU4JNI SVN trunk
精彩评论