调用静态 JNI 方法以从C++返回字符串

call a static JNI method to return a String from C++

本文关键字:C++ 返回 字符串 静态 JNI 方法 调用      更新时间:2023-10-16

我正在尝试在Android中调用以下java方法

public static String getLevelFile(String levelName) { /*body*/}

从 C++ 使用以下 JNI 代码

JniMethodInfoJavaApi methodInfo;
    if (! getStaticMethodInfo(methodInfo, "getLevelFile", "(Ljava/lang/String;)Ljava/lang/String;"))
        {
            return std::string("");
        }
    LOGD("calling getLevelFile");
    jstring returnString = (jstring) methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, levelName.c_str());
    LOGD("returned from getLevelFile");
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
    const char *js = methodInfo.env->GetStringUTFChars(returnString, NULL);
    std::string cs(js);
    methodInfo.env->ReleaseStringUTFChars(returnString, js);
    LOGD("returning Level data");

执行CallStaticMethodObject()时应用程序崩溃。我已经通过使用javap验证了方法签名是否正确。LOGD("calling getLevelFile");打印正常,然后崩溃。我可以做同一类的其他CallStaticVoidMethod(),但不能做这个。知道我做错了什么吗?

你很幸运,Java和Android都使用Unicode字符集。但是,Android(默认情况下)使用UTF-8编码,JNI本质上不支持这种编码。尽管如此,Java 类完全能够在字符集编码之间进行转换。lang.java.String构造函数允许您指定字符集/编码或使用操作系统默认值,当然,在 Android 上,它被编码为 UTF-8。

为了简化它(我更喜欢用 Java 编码,最大限度地减少调用 JNI 库的代码),创建方法的重载并在 Java 中执行一些实现:

private static byte[] getLevelFile(byte[] levelName) {
    return getLevelFile(new String(levelName)).getBytes();
}

现在 JNI 代码只需要处理 jbytearray,无论是参数还是返回值:

JniMethodInfoJavaApi methodInfo;
if (! getStaticMethodInfo(methodInfo, "getLevelFile", "([B)[B"))
{
    return std::string("");
}
LOGD("calling getLevelFile");
int nameLength = levelName.length();
jbyteArray nameBytes = methodInfo.env->NewByteArray(nameLength);
methodInfo.env->SetByteArrayRegion(nameBytes, 0, nameLength, reinterpret_cast<const jbyte*>(levelName.c_str()));
jbyteArray returnString = (jbyteArray) methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, nameBytes);
LOGD("returned from getLevelFile");
methodInfo.env->DeleteLocalRef(methodInfo.classID);
methodInfo.env->DeleteLocalRef(nameBytes);
int returnLength = methodInfo.env->GetArrayLength(returnString);
std::string data;
data.reserve(returnLength);
methodInfo.env->GetByteArrayRegion(returnString, 0, returnLength, reinterpret_cast<jbyte*>(&data[0]));
methodInfo.env->DeleteLocalRef(returnString);
LOGD("returning Level data");
return data;

你不能直接将 null 终止的字符串(从 c_str() 返回)作为参数传递给 Java/JNI 方法。

若要将其传递给方法,请从 null 终止的字符串创建一个jstring(例如使用 NewStringUTF ),然后改为传递该字符串。