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

Aer*_*alo 17 c++ java java-native-interface android android-ndk

我正在构建一个正在构建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);  
    }  
Run Code Online (Sandbox Code Playgroud)

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

理想情况下,我希望字符串/字符串数组在行中"实时"发送,而不是在函数的末尾发送,这样我就不必合并新函数.

我发现的代码能否满足我的需求(通过一些改编)?我正在寻找甚至可能吗?如果是这样,我该怎么办?

编辑/更新: 花了一天时间研究JNI和术语,我想我无法正确地传达我想要在这里实现的内容以及对@ jogabonito的回答/回复的评论.

话虽如此.我正在处理的代码是一个IM客户端,需要将消息和状态更新推送到Android Java应用程序(通过JNI),以便Android应用程序不会轮询更新.我已经设法学习如何设置java代码的函数来调用requrest信息.但是,我不知道如何将新消息或状态信息(jabber节字符串)推送到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);
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*get 14

在@Sam的请求下,这是一种避免使用修改的UTF-8的方法,因为我们不知道这样做是安全的.

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

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

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

对于这种原生方法:

private static native String getJniString();
Run Code Online (Sandbox Code Playgroud)

这是一个实现:

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;
}
Run Code Online (Sandbox Code Playgroud)

JNI很尴尬.所以,如果我们可以将原生字符串为"UTF-8"的知识转移到Java端,我们可以这样做:

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

更简单的实现:

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;
}
Run Code Online (Sandbox Code Playgroud)


jog*_*ito 4

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

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

  2. GetFieldID如果您已经在 J​​ava 代码中定义了一个字符串作为字段,则可以使用和从本机访问它GetObjectField,并且可以使用 更新它SetObjectField。我不知道你如何向你的java代码发出该字段已更新的信号(如果你需要的话)

编辑
您编写的更新函数旨在从java层调用。线索是函数的名称Java_the_package_MainActivity_getJniString。要从本机上下文调用 java 代码,您将需要对 java.langenvobjjava.lang.String 的引用。看看如何在 Android 上用 C 加载我自己的 Java 类?获取此方法的方法。您可能还需要了解如何在 JNI 中使用全局引用