JNI - 将 UChar 类型映射到

发布于 2024-10-17 19:55:15 字数 851 浏览 3 评论 0原文

我有一个 JNI 函数,它返回一个 UChar 数组(来自 ICU4C 库),我想将其转换为 Java 字符数组,以便我可以从 Java 调用它。我不确定问题出在哪里,因为每当我访问这个 JNI 函数时,一切都会崩溃并挂起,但我在任何地方都没有收到错误消息,包括在 logcat 中......非常难以调试!

UChar 数组可以直接映射到 jcharArray 类型吗?另外,我可以将它用作返回类型吗?或者我可以将其作为 JNI 函数随后填充的参数传递吗?

这是我想做的事情的一个片段:

static jint testFunction(JNIEnv* env, jclass c, jobject obj, jcharArray 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);

}

非常感谢任何帮助!

谢谢

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, jcharArray 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

输什么也不输骨气 2024-10-24 19:55:15

JNI 确实令人头疼。从表面上看,你的功能看起来很好。

首先,我注意到您没有使用 offset - 这是代码味道。

其次,您没有释放 UChar 数组。

第三,C 函数或赋值循环可能超出数组范围。


为了帮助定位像这样的突然崩溃,我成功地结合使用了一个很好的 print 语句与控制台。

首先,我向 JNIGlobal 类添加了一个 println 方法:

/** 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);
    }

然后,我向 C 代码添加了一个相应的方法:

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);
    }

然后,我只需在源代码中的每一行调用 println(env,"Some formatted output")看看它能走多远。在我的环境(AS/400)中,当 JVM 在交互式运行期间崩溃时,我只剩下控制台 - 您可能需要在 Java 代码中添加短暂的延迟,以确保在控制台消失之前看到输出。

所以对于你来说,就像这样:

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");
}

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");
}
失去的东西太少 2024-10-24 19:55:15

如果您的目的是从 ICU 获取 UChar* 值并将字符串返回到 Java(我假设这是基于“从 ICU4C 函数在此处填充 str”注释),为什么不只使用 jstring

例如:

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;
}

该示例当然是简化的,但应该说明 UChar* 到 jstring 的转换。这也可以轻松地与 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()));
}

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()));
}
南笙 2024-10-24 19:55:15

dstArray 有多长?如果 len 大于 dstArray.length,c++ 无法检查数组边界,并且会很高兴地破坏进程的内存。

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.

围归者 2024-10-24 19:55:15

ICU4JNI 并未得到积极维护,但您也许可以查看它的示例从 JNI 调用 ICU4C。另请参阅 ICU4JNI SVN 主干

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文