Reputation: 984
Can I pass a string from Java to my C++ routine using JNI function calls and modify its value in the C++ routine?
So far, I have seen examples of returning jstring, which I do not want to do. The other option that I know about is to get the ID of the string variable within C++ and set its value.
At present, I am playing with a function like the following:
JNIEXPORT void JNICALL Java_myexample_ChangeString
(JNIEnv *, jobject obj, jstring strJava)
And I want to change strJava's value. So, essentially what I am asking is if it Java can pass variables by reference and not just by value.
Thanks.
Upvotes: 3
Views: 3418
Reputation: 5499
You can't directly modify a String
parameter, as Java String
s are immutable. What you can do however is place the string in an array, pass the array to the native code, and then in the native code (using some JNI interop methods) create a new Java String
and replace the reference in the array with a reference to the new string:
Caller.java:
package com.mycompany.myapp;
// Prototype for native method.
class MyJNI {
public static native void changeStringsNative(String[] stringArr);
}
void CallNative()
{
String[] myStringArr = new String[1] {"Original"};
System.out.println("Original (Java): " + myStringArr[0]);
MyJNI.changeStringsNative(myStringArr);
System.out.println("Changed (Java): " + myStringArr[0]);
}
Callee.c
#include <jni.h>
#include <stdio.h>
// Helper macro so we only need to declare class name once.
#define JNIFUNCTION(sig) Java_com_mycompany_myapp_MyJNI##sig
JNIEXPORT void JNICALL JNIFUNCTION(changeStringsNative(JNIEnv *env, jobject obj,
jobjectArray myStringArr))
{
int stringCount = env->GetArrayLength(myStringArr);
for (int i = 0; i < stringCount; i++) {
jstring myString = (jstring)(env->GetObjectArrayElement(myStringArr, i));
const char *myStringOriginalC = env->GetStringUTFChars(myString, NULL);
printtf("Original (native): %s\n", myStringOriginalC);
env->ReleaseStringUTFChars(myString, myStringOriginalC);
// Now do something in native to transform the string. For example, a printf involving the original.
char *myStringChangedC = NULL;
if (asprintf(&myStringChangedC, "Different from the %s!\n", myStringOriginalC) == -1) {
// asprintf malloc error.
return;
}
printtf("Changed (native): %s\n", myChangedStringC);
// Create a new jstring and put into the array.
env->SetObjectArrayElement(myStringArr, i, env->NewStringUTF(myChangedStringC));
free(myStringChangedC); // must free after asprintf's malloc.
}
return true;
}
Output to stdout:
Original (Java): Original
Original (native): Original
Changed (native): Different from the Original!
Changed (Java): Different from the Original!
Upvotes: 0
Reputation: 4014
So, it seems that what you really want to do is return more than one value from a native method. There is no direct way to do this, but there are a few indirect ways:
boolean
, create a class that contains both a boolean
and a String
, and have your native code create and return an instance of that class.String[]
as an argument, and have the native code set its string result as the first element of that array.#setStringValue(String)
method, and pass that into your native method, which will then call that method to supply the string result. Once the native call completes, the Java code that created the helper class can extract the string value.Of these possibilities, the one-element array is probably the easiest to handle from JNI code because it doesn't involve looking up additional classes or methods. However, if I wanted to return extra information from a non-native method I would probably return an instance of a new class containing both results.
Upvotes: 3
Reputation: 81349
Java strings are immutable by design, you cannot change them, not even with JNI.
Upvotes: 6