【问题标题】:Returning an arraylist of string from Native java to JNI从 Native java 返回一个字符串数组列表到 JNI
【发布时间】:2015-10-29 05:26:27
【问题描述】:
ArrayList<String> myArraylist;          

public ArrayList<String> getData(){
    myArraylist = new ArrayList<String>();         
    myArraylist.add("1267982563");
    myArraylist.add("2345678");
    myArraylist.add("5432789");
    return myArraylist;
}

如何在 JNI 端从上述方法中获取每个项目并推送到向量并从 JNI 返回到 JNI 层中的其他 CPP 调用。

【问题讨论】:

  • "myArraylist&lt;String&gt;" ArrayList<String>。这只是一个String
  • 如果它只是一个字符串的 ArrayList,为什么不使用 toArray() 转换为字符串数组并将其作为 jobjectArray 传递给 JNI?然后就可以使用GetObjectArrayElement等JNI对象数组操作函数了。
  • @SACH 您真的应该考虑将以下答案标记为已接受的解决方案,因为它全面解决了您的问题。

标签: java arraylist java-native-interface native


【解决方案1】:

将 ArrayList 转换为 std::vector<:string>:

jclass java_util_ArrayList;
jmethodID java_util_ArrayList_;
jmethodID java_util_ArrayList_size;
jmethodID java_util_ArrayList_get;
jmethodID java_util_ArrayList_add;
thread_local JNIEnv *env;

void init() {
  java_util_ArrayList      = static_cast<jclass>(env->NewGlobalRef(env->FindClass("java/util/ArrayList")));
  java_util_ArrayList_     = env->GetMethodID(java_util_ArrayList, "<init>", "(I)V");
  java_util_ArrayList_size = env->GetMethodID (java_util_ArrayList, "size", "()I");
  java_util_ArrayList_get  = env->GetMethodID(java_util_ArrayList, "get", "(I)Ljava/lang/Object;");
  java_util_ArrayList_add  = env->GetMethodID(java_util_ArrayList, "add", "(Ljava/lang/Object;)Z");
}

std::vector<std::string> java2cpp(jobject arrayList) {
  jint len = env->CallIntMethod(arrayList, java_util_ArrayList_size);
  std::vector<std::string> result;
  result.reserve(len);
  for (jint i=0; i<len; i++) {
    jstring element = static_cast<jstring>(env->CallObjectMethod(arrayList, java_util_ArrayList_get, i));
    const char *pchars = env->GetStringUTFChars(element, nullptr);
    result.emplace_back(pchars);
    env->ReleaseStringUTFChars(element, pchars);
    env->DeleteLocalRef(element);
  }
}

将 ArrayList 从 JNI 推回 Java

如果您不在 JNI 中修改此列表,那么最好的策略是简单地在您的本地代码中的某处保留对它的全局引用。如果您稍作修改,请保持此 jobject 始终是最新的(您可能需要方法 java_util_ArrayList_addjava_util_ArrayList_set)。

如果您选择从 scratch 向量重构列表,您将展开上述方法:

jobject cpp2java(std::vector<std::string> vector) {
  jobject result = env->NewObject(java_util_ArrayList, java_util_ArrayList_, vector.size());
  for (std::string s: vector) {
    jstring element = env->NewStringUTF(s.c_str());
    env->CallBooleanMethod(result, java_util_ArrayList_add, element);
    env->DeleteLocalRef(element);
  }
  return result;
}

无论如何,在使用 Jni 时要小心线程,始终连接本地线程并在本地线程被销毁之前分离。

【讨论】:

  • @AlexCohn 关于你的最后一句话,你能举个例子吗?对于我的情况,我想将 JNIEnv* 指针从本机方法传递给辅助方法(在同一个 C++ 文件中)。它基本上会做你正在做的事情,除了例如 cpp2java 方法会有一个 JNIEnv* 指针作为额外的参数。这安全吗?或者你的最后一句话到底指的是什么?
  • @ifma JNIEnv* 仅在一个线程上有效。您可以保留JavaVM* 并使用AttachThread() 获取JNIEnv* 用于从C++ 到Java 的调用,请参阅stackoverflow.com/a/12900986/192373
  • @Pixel 我相信在这种情况下,static_cast 更好地表达了程序员的意图。所有 C++ 支持都是事后才添加到 JNI 中的,因此集成永远不会干净。我知道的最好方法是禁用在 JNI 'classes' static_cast 上下文中弹出的每一行的特定警告。
  • @Pixel 见stackoverflow.com/a/65764737/192373,它还显示了如何禁用投诉。
  • @Raii 我不明白你的问题。方法 ID 是与线程无关的常量。 JNIEnv * 是每个线程。 java_util_ArrayList 是一个 Java 对象。如果获取全局引用,可以跨线程和JNI调用使用,但是FindClass函数返回的是局部引用,所以我调用NewGlobalRef
猜你喜欢
  • 2019-11-28
  • 1970-01-01
  • 1970-01-01
  • 2017-11-30
  • 2011-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-09
相关资源
最近更新 更多