NewString () and NewStringUTF (), showing that the error is not valid. Modified UTF-8:
I am trying to transfer char*
from C ++ to java using JNI in android. I tried several ways to pass this 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 the java side, I am getting some garbage value. I don't understand how to get this array char
in Java.
1) yours is data
just not a valid UTF-8 string. Not every char array is automatically valid UTF-8. You are probably using it as a single byte encoding (like ISO or Windows CP), or not reading the string at all.
2) should be ok, but show the code that fills in trackID
from data
. The fact that you need to hard-code it to jbyte*
is suspicious. This code may be correct, but you could also be wrong on the Java side:
If data
it is not a readable string, or is in a single byte encoding that is not the "platform default" java.lang.String (byte []) the constructor cannot make it a human readable string! In this case, you should switch to UTF-8 on the C side. You will also free yourself from the dependency on platform-specific encoding (which may be completely different).
I would suggest data
instead trackID
.
env->SetByteArrayRegion(trackIDArray, 0, strlen(data), (const jbyte*)data);
Then you have bytes, and on the java side what encoding might look like - with a hex dump or some other inspection.
Further:
String s = new String(data, "Cp1252"); // Or so.
NewStringUTF expects you to pass a modified UTF-8 string. You are probably trying to stream UTF-8.
There are several ways to fix this: The most obvious is to encode the string for UTF-8, changed to C ++, before transferring it to Java.
Another way is to pass it to Java as a byte array and use the String constructor to convert it from UTF-16.
The second way might be more efficient since at the end Java uses UTF-16 to represent strings.
As an alternative approach, you can convert the string to UTF-16 in C ++ and pass it to the JNI newString function, which expects UTF-16.
I have put very large byte sources (> 1KB) behind the JNI like this:
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 so that it is up to you to import the native function:
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 deal with a buffered reader is up to you too, a small example:
Strig line = buffer.readLine();
while ((line = buffer.readLine()) != null) {
//play around with 'line'
}