Search code examples
javaandroidcandroid-ndkjava-native-interface

How to I call String.getBytes() in JNI? I want to get byte array in JNI but the CallByteMethod just returns jbyte and not the jbytearray


I am trying to call String.getBytes() method to get byte array in JNI from a string object. The JNI has methods CallByteMethod, CallByteMethodV and CallByteMethodA which returns jbyte but it has no methods to return a java byte array.

I have tried calling CallByteMethod method, but I get an error

JNI DETECTED ERROR IN APPLICATION: use of deleted local reference 0xd5ec7fe1

The other code I tried was using a cast of jbytearray like this

jbyteArray keyBytes = (jbyteArray)(*env)->CallByteMethod(env, stringValue, getBytesMId);

since the IDE showed a warning

Taking pointer from integer without a cast.

but then I get a different error that says

JNI DETECTED ERROR IN APPLICATION: the return type of CallByteMethod does not match byte[] java.lang.String.getBytes()

Below is my code:

JNIEXPORT jstring JNICALL
Java_net_jni_test_MainActivity_callTest(JNIEnv *env, jobject instance) {

    jstring stringValue = "test";

    jclass stringClass = (*env)->FindClass(env, "java/lang/String");
    jmethodID getBytesMId = (*env)->GetMethodID(env, stringClass, "getBytes", "()[B");

    jbyteArray keyBytes = (*env)->CallByteMethod(env, stringValue, getBytesMId);

    return (*env)->NewStringUTF(env, "1111");
}

Solution

  • Just spot some errors from your code:

    1. Below line is wrong:

      jstring stringValue = "test";
      

      And it should be like below:

      jstring stringValue = (*env)->NewStringUTF(env, "test");
      
    2. Use CallObjectMethod to get the jbyteArray, remember to cast the return type to jbyteArray. See below:

      jbyteArray keyBytes = (jbyteArray)(*env)->CallObjectMethod(env, stringValue, getBytesMId);
      
    3. Below is a screenshot showing the expected result.

      enter image description here

    For full source:

    JNIEXPORT jstring JNICALL
    Java_net_jni_test_MainActivity_callTest(JNIEnv *env, jobject instance) {
        jstring stringValue = (*env)->NewStringUTF(env, "test");
    
        jclass stringClass = (*env)->FindClass(env, "java/lang/String");
        jmethodID getBytesMId = (*env)->GetMethodID(env, stringClass, "getBytes", "()[B");
    
        jbyteArray keyBytes = (jbyteArray)(*env)->CallObjectMethod(env, stringValue, getBytesMId);
    
        // determine the needed length and allocate a buffer for it
        jsize num_bytes = (*env)->GetArrayLength(env, keyBytes);
    
    
        // obtain the array elements
        jbyte* elements = (*env)->GetByteArrayElements(env, keyBytes, NULL);
        if (!elements) {
            // handle JNI error ...
        }
    
        for(int i = 0; i < num_bytes; i++) {
            char ch = elements[i];
            ALOGI("arrayLength: %c", ch);
        }
    
        // Do not forget to release the element array provided by JNI:
        (*env)->ReleaseByteArrayElements(env, keyBytes, elements, JNI_ABORT);
    }
    

    Please note the difference of C++ JNI and C JNI. E.g. C style JNI have below method convention:

    jmethodID getBytesMId = (*env)->GetMethodID(env, stringClass, "getBytes", "()[B");
    

    But C++ is like below:

    jmethodID getBytesMId = env->GetMethodID(stringClass, "getBytes", "()[B");