12
ArrayList<String> myArraylist;          

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

How to get the each items from the above method in JNI side and Push to vector and return from the JNI to other CPP calls in the JNI layer.

3
  • 1
    "myArraylist<String>" <-- that's not an ArrayList<String>. it's just a String. Commented Oct 29, 2015 at 7:07
  • 1
    If it's just an ArrayList of Strings, why not convert to a string array using toArray() and pass that to JNI as as a jobjectArray instead? Then you can use the JNI object array manipulation functions such as GetObjectArrayElement. Commented Oct 29, 2015 at 8:17
  • @SACH You really should consider marking the below answer as the accepted solution, as it comprehensively solved your problem. Commented Jun 8, 2016 at 13:47

1 Answer 1

28

Convert ArrayList to std::vector<std::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);
  }
}

Push ArrayList from JNI back to Java

If you don't modify this list in JNI, then the best strategy would be to simply keep a global reference to it somewhere in your native code. If you modify it a little, keep this jobject always up-to-date (you will probably need the methods java_util_ArrayList_add or java_util_ArrayList_set).

If you choose to reconstruct the list from scratch the vector, you will unwind the above method:

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

At any rate, be careful with threads when you work with Jni, always attach your native threads and detach before the native thread is destroyed.

Sign up to request clarification or add additional context in comments.

8 Comments

@AlexCohn Regarding your last sentence can you provide an example of this? For my case I want to pass the JNIEnv* pointer around from a native method to a helper method (in the same C++ file). It would essentially do what you're doing, except the cpp2java method for example would have a JNIEnv* pointer as an extra argument. Is this safe? Or what is your last sentence referring to exactly?
@ifma JNIEnv* is only valid on one thread. You can keep a JavaVM* and use AttachThread() to obtain a JNIEnv* for calls from C++ to Java, see stackoverflow.com/a/12900986/192373.
@Pixel I believe that in this case, static_cast better expresses the programmer's intent. All C++ support having been added to JNI as an afterthought, the integration is never clean. The best approach I know is to disable the specific warning for each line where it pops up in the context of JNI 'classes' static_cast.
@Pixel see stackoverflow.com/a/65764737/192373, it also shows how the complaint can be disabled.
@Raii I don't understand your question. Method IDs are thread-independent constants. The JNIEnv * is per thread. java_util_ArrayList is a Java object. If you obtain a global reference, you can use it across threads and JNI calls, but the FindClass function returns a local reference, thus I call NewGlobalRef.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.