Sagar Ekbote
Sagar Ekbote

Reputation: 305

NewString() & NewStringUTF() showing error not valid Modified UTF-8:

I am trying to pass char* from C++ to java using JNI in android. I have tried number of ways to pass that data

1) Using NewStringUTF:

const char* data = getData(); // this method returns a char array.
env->NewStringUTF(data);

Executing above code throws below error

JNI WARNING: input is not valid Modified UTF-8: illegal continuation byte 0x70.

2) Using NewString:

const char* data = getData(); // this method returns a char array.
// passing a byte array to java
jbyteArray trackIDArray = env->NewByteArray(strlen(data));
env->SetByteArrayRegion(trackIDArray, 0, strlen(data), (const jbyte*)trackID);

On java side, I am getting some garbage value. I don't understand how to get this char array to Java.

Upvotes: 4

Views: 12217

Answers (4)

Nikolai Ehrhardt
Nikolai Ehrhardt

Reputation: 726

I put very big bytesources (>2kbyte) behind the JNI like this :

Content of a csv-table:

R"xxx(tbl_Cbla,Column 02,Column 03,Column 04
sdfsdsad,sdfasd,dsfaddf,fdsasdf,fafasa
18,1,10,8,0)xxx"`
std::string data1 =
#include "big_table1.csv" 
;

std::string data2 =
#include "big_table2.csv"
;

extern "C" JNIEXPORT jbyteArray JNICALL
Java_com_example_bigData_rawResource(
        JNIEnv *env,
        jobject /* this */, jint index) {

    std::string values;

    switch (index) {
        case 0: {values = data1;break;}
        case 1: {values = data2;break;}
    }

    int byteCount = values.length();
    jbyteArray ret = env->NewByteArray(byteCount);
    const jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(values.c_str());
    env->SetByteArrayRegion (ret, 0, byteCount, pNativeMessage);
    return ret;
}

In Java you can get it back like this, to import the native function is up to you:

ByteArrayInputStream bis = null;
try {

   bis = new ByteArrayInputStream(rawResource(1);
} catch (Exception e) {
   e.printStackTrace();
}
BufferedReader buffer = new BufferedReader(new InputStreamReader(bis,   Charset.forName("UTF-8")));

To to handle the buffered reader is also up to you, small exg.:

Strig line = buffer.readLine(); 
while ((line = buffer.readLine()) != null) {
 //play around with 'line'
}

Upvotes: 0

Sergey Dryganets
Sergey Dryganets

Reputation: 919

NewStringUTF expects you to pass a Modified UTF-8 string. You are likely trying to pass UTF-8.

There are multiple ways to fix it: Most obvious one is to encode the string to UTF-8 modified in C++ before passing it to Java.

Another way is to pass it to Java as a byte array and use String constructor to convert it from UTF-16.

The second way might be more efficient as in the end Java uses UTF-16 for string representation.

As an alternative approach, you could convert the string to UTF-16 in C++ and pass it to newString JNI function which expects UTF-16.

Upvotes: 3

Joop Eggen
Joop Eggen

Reputation: 109597

I would suspect data instead of trackID.

env->SetByteArrayRegion(trackIDArray, 0, strlen(data), (const jbyte*)data);

Then you have the bytes and on the java side may look what encoding it is - by a hex dump or other inspection.

Later:

String s = new String(data, "Cp1252"); // Or so.

Upvotes: 2

Pavel Zdenek
Pavel Zdenek

Reputation: 7293

1) your data is simply not a valid UTF-8 string. Not every char array is automatically a valid UTF-8. You probably have it in some single-byte encoding (like ISO or Windows CP), or it's not a readable string at all.

2) should be ok, but show the code which fills trackID from data. The fact that you need to hard typecast it to jbyte* is suspicious. This code might be correct, but you can make a mistake on Java side too:

If data is not a readable string or is in single-byte encoding which is not "platform's default charset" java.lang.String(byte[]) constructor won't be able to make a readable string out of it! In that case, you must convert to UTF-8 on C side. You will also release yourself from the dependency on platform specific encoding (which may be wildly different).

Upvotes: 2

Related Questions