通过 JNI C++字符串发送到 Java

我正在一个构建Android应用程序的项目C++方面工作。有一些信息(通过字符串和字符串数组)我需要传递给Java应用程序(通过JNI)。我以前从未这样做过,在相反方向上工作的人没有C++的经验,并承认他们无法真正提供帮助。

我确实找到了以下代码(从这里)

 #include <jni.h>  
    #include "ArrayHandler.h"  

    JNIEXPORT jobjectArray JNICALL Java_ArrayHandler_returnArray (JNIEnv *env, jobject jobj){        
      jobjectArray ret;  
      int i;  
      char *message[5]= {"first","second","third","fourth","fifth"};  
      ret= (jobjectArray)env->NewObjectArray(5,env->FindClass("java/lang/String"),env->NewStringUTF(""));  

      for(i=0;i<5;i++) {  
        env->SetObjectArrayElement(ret,i,env->NewStringUTF(message[i]));  
      }  
      return(ret);  
    }  

但这对我来说毫无意义。大多数情况下,我不确定我应该如何将其纳入程序C++方面,并且我无法确切地理解它是如何工作的。代码是否在执行行时发送消息?还是在 for 循环内执行行期间?return(ret);

理想情况下,我希望字符串/字符串数组以“实时”方式发送,而不是在函数结束时发送,这样我就不必合并新函数。

我找到的代码是否适用于我想要的(经过一些调整)?我想要的东西是可能的吗?如果是这样,我该怎么做?

编辑/更新:我花了一天时间研究JNI和术语,我认为我未能正确传达我希望在这里实现的目标,并作为对@jogabonito的答案/回复的评论。

话虽如此。我正在处理的代码适用于IM客户端,该客户端需要将消息和状态更新推送到Android Java应用程序(通过JNI),以便Android应用程序不会轮询更新。我已经设法学会了如何设置java代码调用以请求信息的函数。但是,我不知道如何在新消息或状态信息(jabber节字符串)进入java代码时将其推送到java代码。我看到的所有关于如何执行此操作的代码(例如,请参阅下文)似乎都需要从java代码(env,class,methodid等)中获取信息。

对我来说,当这不是调用函数的java代码,而是我的c ++代码时,这应该如何成为可能,这对我来说是没有意义的。任何解释/帮助将不胜感激。

#include <string.h>
#include <stdio.h>
#include <jni.h>

jstring Java_the_package_MainActivity_getJniString( JNIEnv* env, jobject obj){

    jstring jstr = (*env)->NewStringUTF(env, "This comes from jni.");
    jclass clazz = (*env)->FindClass(env, "com/inceptix/android/t3d/MainActivity");
    jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
    jobject result = (*env)->CallObjectMethod(env, obj, messageMe, jstr);

    const char* str = (*env)->GetStringUTFChars(env,(jstring) result, NULL); // should be released but what a heck, it's a tutorial :)
    printf("%s\n", str);

    return (*env)->NewStringUTF(env, str);
}

答案 1

根据@Sam的要求,这里有一种方法可以避免使用修改后的UTF-8,因为我们不知道这样做是安全的。

NewStringUTF 从其修改后的 UTF-8 编码创建一个字符串。将其与用户数据一起使用是不正确的 - 它不太可能使用修改后的UTF-8进行编码。我们只能希望数据中的字符受到限制,以保持其兼容性。相反,我们可以正确地转换它。

JNI 在其整个 API 中使用修改后的 UTF-8 字符串。我们可以使用我们知道是兼容的字符串,特别是Java标识符的文字(除了不是所有的货币符号)。

下面是两个本机方法实现。第二种在大多数方面更好。

对于此本机方法:

private static native String getJniString();

下面是一个实现:

JNIEXPORT jstring JNICALL 
Java_the_Package_MainActivity_getJniString(JNIEnv *env, jclass)
{   
    std::string message = "Would you prefer €20 once "
                          "or ₹10 every day for a year?";

    int byteCount = message.length();
    jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
    jbyteArray bytes = env->NewByteArray(byteCount);
    env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);

    // find the Charset.forName method:
    //   javap -s java.nio.charset.Charset | egrep -A2 "forName"
    jclass charsetClass = env->FindClass("java/nio/charset/Charset");
    jmethodID forName = env->GetStaticMethodID(
      charsetClass, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
    jstring utf8 = env->NewStringUTF("UTF-8");
    jobject charset = env->CallStaticObjectMethod(charsetClass, forName, utf8);

    // find a String constructor that takes a Charset:
    //   javap -s java.lang.String | egrep -A2 "String\(.*charset"
    jclass stringClass = env->FindClass("java/lang/String");
    jmethodID ctor = env->GetMethodID(
       stringClass, "<init>", "([BLjava/nio/charset/Charset;)V");

    jstring jMessage = reinterpret_cast<jstring>(
      env->NewObject(stringClass, ctor, bytes, charset));

    return jMessage;
}

JNI很尴尬。因此,如果我们能够将本机字符串为“UTF-8”的知识移动到Java端,我们可以这样做:

private static String getJniString2()
{
    return new String(getJniStringBytes(), Charset.forName("UTF-8"));
}
private static native byte[] getJniStringBytes();

以及更简单的实现:

JNIEXPORT jbyteArray JNICALL Java_the_Package_MainActivity_getJniStringBytes(JNIEnv *env, jclass)
{   
    std::string message = "Would you prefer €20 once "
                          "or ₹10 every day for a year?";

    int byteCount = message.length();
    jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
    jbyteArray bytes = env->NewByteArray(byteCount);
    env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);

    return bytes;
}

答案 2

在共享的函数中,在 c++ 代码中,您将使用 创建一个对象数组。然后在 for 循环中,使用 创建一个字符串并将其存储在数组中的索引处。到目前为止,您的对象数组只对 c++ 代码知道,而对 Java 代码不了解。只有当您返回它时,您的Java应用程序才能访问它。
我可以想出几种将字符串从c ++发送到java的方法,尽管它可能并不完全符合您的意图。NewObjectArrayNewStringUTFSetObjectArrayElement

  1. 将字符串数组传递给本机函数。在本机代码中,您可以使用 访问每个元素,并使用 更新它。这可能毫无意义,因为你最终不得不调用一个我推测你不想要的函数。GetObjectArrayElementSetObjectArrayElement

  2. 如果已在 Java 代码中将字符串定义为字段,则可以使用 和 从本机访问该字符串,并且可以使用 更新它。我不知道您将如何发出java代码信号,表明该字段已更新(如果需要)GetFieldIDGetObjectFieldSetObjectField

编辑
您编写的更新函数旨在从 Java 层调用。这方面的线索是函数的名称 。要从本机上下文中调用 java 代码,您需要引用 java 和 从 java。看看如何在 Android 上的 C 语言中加载我自己的 Java 类?以获取一种方法。您可能还必须查找如何在 JNI 中使用全局引用Java_the_package_MainActivity_getJniStringenvobj


推荐