I am trying to call the following Java method (from an external jar file) from C++
Game game = GameLoader.loadGameFromName("My game name");
Where the function signature for loadGameFromName is
public static Game loadGameFromName(String name)
and GameLoader is a Java public final class. Also Game is a public final class.
The code I have used to attempt to call this method is
jclass gameLoader = env->FindClass("player/GameLoader");
jmethodID mid = env->GetStaticMethodID(gameLoader,"loadGameFromName","(Ljava/lang/String;)Lgame/Game");
Here the gameLoader class was successfully found in the first line however the loadGameFromName method was not found in the second line.
Also, it might be relevant that I was able to successfully call a different method from GameLoader which was
String[] games = GameLoader.listGames()
with the following code
jclass gameLoader = env->FindClass("player/GameLoader");
jmethodID mid = env->GetStaticMethodID(gameLoader,"listGames","()[Ljava/lang/String;");
jobjectArray stringArray = (jobjectArray) env->CallStaticObjectMethod(gameLoader,mid);
So that code works fine. So it looks like the main difference here is that loadGameFromName returns a Game object rather than a Java string object. Also, it might be relevant to note that I am able to successfully find the Game class with jclass game = env->FindClass("game/Game");.
Is there something wrong with the way I am calling GetStaticMethodID for the loadGameFromName method? I have tried to search for examples of calling Java methods with JNI that return arbitrary Java objects like Game but I couldn't find any.
I have to write some Android platform specific code in Qt and need to use JNI. I have a problem with how to create an array of some object. In this case I want to construct an array of strings from C++.
In the two code snippets below the first one creates a java string and it works as expected. In the second code snippet I want to create a java string array, but I get the debug message: "Java string array not valid" so I assume the signature and/or parameters passed to the "QAndroidJniObject javaStringArray()" function is not correct.
I have been looking at the documentation, but was not able to find or properly understand how to do this.
I assume I have to send in the size of the java string array object I want to construct as well.
Any help is appreciated!
QAndroidJniObject javaString("java/lang/String");
if (!javaString.isValid()) {
qDebug() << "Java string not valid";
return false;
}
QAndroidJniObject javaStringArray("[Ljava/lang/String;");
if (!javaStringArray.isValid()) {
qDebug() << "Java string array not valid";
return false;
}
The QAndroidJniObject constructor you are using takes a class name, so I'm afraid passing a string array signature won't work. You'll probably have to get your hands dirty and call JNI NewObjectArray(). Try something like:
QAndroidJniEnvironment env;
jobjectArray stringArray = env->NewObjectArray(5, env->FindClass("java/lang/String"), NULL);
QAndroidJniObject jniArray = QAndroidJniObject::fromLocalRef(stringArray);
// ...
This would create an array of 5 null strings, and transfer the ownership to QAndroidJniObject if you prefer, or else you'll have to take care of calling DeleteLocalRef().
I have a C routine that is calling a Java module through the JNI invocation interface. I've been having an issue where the call to the Java method has been returning a NULL string when using the C module and the JNI, but when I use Java at the command line, the Java module returns an error and a default value.
Here is the data returned when I bypass the C code and call the Method through Java at the command line
$ java ClassName "This" "is" "my" "test" "string"
Exception on SomethingElse.Method: [Function: DifferentMethod]ExceptionClassException: ExceptionClassExceptionException: [Function: CFBDynamicMessage::getCodePage]codePageBytes is NULL, for UTF-8
Returned String -- data I'm trying to get
0.0| |0.0| |0.0|| |0.0|0| |0.0| | ||0.0|0|0|0|0|0|0|0|0|0|0||
I need to get the returned string, even in the event of an error on the java side. To try to see what is going on in the C and JNI, I turned up the debugging level for the JNI:
options[1].optionString = "-Xdebug"; /* print JNI-related messages */
options[2].optionString = "-Xlog:all";
// this next line allows us to continue processing after the Java
// error. Unfortunately the code then SegFaults further on.
options[3].optionString = "-Xcheck:jni:nonfatal";
options[4].optionString = "-verbose:jni";
The JNI starts the JVM, finds the class and the method and builds the Java string I need to pass to the method, but when I try to execute the method, I get the following errors:
JVMJNCK048E JNI error in CallObjectMethod/CallObjectMethodV: Ineligible receiver
JVMJNCK080E Error detected in the outermost frame of an attached thread
JVMJNCK023E JNI error detected. Continuing...
Segmentation fault (core dumped)
Here is the C Code (I am cutting out some error checking and whitespace for brevity):
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
cls = (*env)->FindClass(env, "GenericClassName");
mid = (*env)->GetMethodID(env, cls, "execute", "(Ljava/lang/Object;)Ljava/lang/Object;");
jstr = (*env)->NewStringUTF( env, "This|is|my|test|string|");
// This next line is the one that throws the ineligible receiver error
jstring resultStr = (*env)->CallObjectMethod(env, cls, mid, jstr);
// ExceptionOccurred(env) is false; no errors are displayed
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
const char *nativeString = (*env)->GetStringUTFChars(env, resultStr, JNI_FALSE);
// This next line calls the SegFault
fprintf(stdout, "%s\n", *nativeString);
I've used gdb to look at the core dump created by the SegFault and here are the what I think are the pertinent results:
print nativeString
$1 = 0x8dfaa80 "[Ljava.lang.Class;#34ca34ca"
print *nativeString
$2 = 91 '['
here is the definition of the java method I'm calling:
public Object execute (Object args) throws Exception { ... }
Any help or insight you can provide with this issue will be greatly appreciated. When I try to Google for the ineligible receiver error, I get a bunch of links about football, but nothing in the JNI or Java in general. I've spent the last few days scouring the Stack Overflow website, and although there were some promising questions asked, they were all related to Android and didn't provide any additional assistance in resolving my issue.
As two final notes, even if you can't help, thanks for reading this far and my environment is a 64bit RHEL server, but the C code and the JVM are both running as 32bit applications.
TL;DR - C is calling a Java Method through the JNI and I am getting an error code (JNI error in CallObjectMethod/CallObjectMethodV: Ineligible receiver) that results in Google telling me all about all the sports I can't watch because I'm working on this issue.
You're supplying a class instead of an object. So you're trying to call Class<GenericClassName>.execute(Object arg). You need an instance of the class.
I am working on the C++ side of a project that is building an Android application. There is some information (via strings and string arrays) that I need to pass to the Java application (via JNI). I have never done this before, and the people working in the reverse direction have no experience with C++ and admit that they cannot really help.
I did find the following code (from here)
#include <jni.h>
#include "ArrayHandler.h"
JNIEXPORT jobjectArray JNICALL Java_ArrayHandler_returnArray (JNIEnv *env, jobject jobj){
jobjectArray ret;
int i;
char *message[5]= {"first","second","third","fourth","fifth"};
ret= (jobjectArray)env->NewObjectArray(5,env->FindClass("java/lang/String"),env->NewStringUTF(""));
for(i=0;i<5;i++) {
env->SetObjectArrayElement(ret,i,env->NewStringUTF(message[i]));
}
return(ret);
}
But this makes no sense to me. Mostly, I am not sure how I am supposed to incorporate this into the C++ side of the program and I am failing to understand exactly how this works. Is the code sending out the message upon execution of the return(ret); line? Or during the execution of the line within for loop?
Ideally, I would like the string/string array to be sent out "live" in line and not at the end of a function so that I do not have to incorporate a new function.
Will the code I found work for what I want (with some adaptation)? Is what I am looking for even possible? If so, how can I do it?
EDIT/UPDATE:
Having spent the day looking into JNI and the terminology, I think I have failed to correctly communicate what I am looking to achieve both here and as a comment to #jogabonito's answer/reply.
That being said. The code I am working on is for an IM client that will need to push message and presence updates to the Android java application (via JNI) so that the Android application does not poll for updates. I have managed to learn how to setup the functions for the java code to call to requrest information. However, I do not have any idea how to push new message or presence information (jabber stanza strings) to the java code when it comes in. All the code that I have seen on how to do this (see below for example) seems to require getting information from the java code (env, class, methodid, etc).
It does not make sense to me how this is supposed to be possible when it is not the java code calling the function, but my c++ code. Any explanation/help would be very much appreciated.
#include <string.h>
#include <stdio.h>
#include <jni.h>
jstring Java_the_package_MainActivity_getJniString( JNIEnv* env, jobject obj){
jstring jstr = (*env)->NewStringUTF(env, "This comes from jni.");
jclass clazz = (*env)->FindClass(env, "com/inceptix/android/t3d/MainActivity");
jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
jobject result = (*env)->CallObjectMethod(env, obj, messageMe, jstr);
const char* str = (*env)->GetStringUTFChars(env,(jstring) result, NULL); // should be released but what a heck, it's a tutorial :)
printf("%s\n", str);
return (*env)->NewStringUTF(env, str);
}
At the request of #Sam, here is a method that avoids using modified UTF-8 because we don't know that it is safe to do so.
NewStringUTF creates a string from its modified UTF-8 encoding.
It is not correct to use it with user data--it's unlikely to be encoded with modified UTF-8. We could just hope that the characters in the data are restricted to keep it compatible. Instead, we can convert it properly.
JNI uses modified UTF-8 strings throughout its API. We can use strings we know are compatible, particularly literals for Java identifiers (except not all currency symbols).
Below are two native method implementations. The second is better in most ways.
For this native method:
private static native String getJniString();
Here is an implementation:
JNIEXPORT jstring JNICALL
Java_the_Package_MainActivity_getJniString(JNIEnv *env, jclass)
{
std::string message = "Would you prefer €20 once "
"or ₹10 every day for a year?";
int byteCount = message.length();
jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
jbyteArray bytes = env->NewByteArray(byteCount);
env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);
// find the Charset.forName method:
// javap -s java.nio.charset.Charset | egrep -A2 "forName"
jclass charsetClass = env->FindClass("java/nio/charset/Charset");
jmethodID forName = env->GetStaticMethodID(
charsetClass, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
jstring utf8 = env->NewStringUTF("UTF-8");
jobject charset = env->CallStaticObjectMethod(charsetClass, forName, utf8);
// find a String constructor that takes a Charset:
// javap -s java.lang.String | egrep -A2 "String\(.*charset"
jclass stringClass = env->FindClass("java/lang/String");
jmethodID ctor = env->GetMethodID(
stringClass, "<init>", "([BLjava/nio/charset/Charset;)V");
jstring jMessage = reinterpret_cast<jstring>(
env->NewObject(stringClass, ctor, bytes, charset));
return jMessage;
}
JNI is awkward. so, if we can move the knowledge that the native string is "UTF-8" to the Java side, we can do this:
private static String getJniString2()
{
return new String(getJniStringBytes(), Charset.forName("UTF-8"));
}
private static native byte[] getJniStringBytes();
And the much simpler implementation:
JNIEXPORT jbyteArray JNICALL Java_the_Package_MainActivity_getJniStringBytes(JNIEnv *env, jclass)
{
std::string message = "Would you prefer €20 once "
"or ₹10 every day for a year?";
int byteCount = message.length();
jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
jbyteArray bytes = env->NewByteArray(byteCount);
env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);
return bytes;
}
In the function you shared, in your c++ code you are creating an object array with NewObjectArray. Then in your for-loop you are creating a string with NewStringUTF and storing it at an index in your array using SetObjectArrayElement. Until now, your object array is only known to your c++ code and not to your java code. Only when you return it will your java app get access to it.
I can think of a couple of ways to send the string to java from c++, though it may not be exactly what you intended.
Pass a String array to your native function. In you native code you can access each element using GetObjectArrayElement and update it using SetObjectArrayElement. This will probably be pointless since you end up having to call a function which I persume you do not want.
If you already have a string defined as a field in your java code, from your native get access to it using GetFieldID and GetObjectField, and you can update it using SetObjectField. I dont know how you will signal your java code that the field has been updated though ( if you need it)
EDIT
The updated function which you have written is meant to be called from the java layer. The clue for this is name of the function Java_the_package_MainActivity_getJniString. To call java code from a native context, you will need references to the env and obj from java. Have a look at How do I load my own Java class in C on Android? for an approach to get this. You will also probably have to look up how to use global references in JNI
You can convert a c-string into a jstring and return it. An example would look something along the lines of:
JNIEXPORT jstring JNICALL Java_Class_Method(jstring data)
{
// jstring to char *
const char *cStr = (*env)->GetStringUTFChars(env, data, NULL);
// convert char * to jstring and return it
return ((*env)->NewStringUTF(env, cStr));
}
Typically with JNI the calls go from the JVM into the C code. The normal paradigm would be:
Java programmers make a Java class with several methods declared as native (no implementation)
Java programmers compile the class with javac
Java programmers run javah against the compiled .class file, this produces a .h header file
C programmer #include the new header file and implement the interface
The only examples I have seen of doing this in the reverse direction (C code initiating contact with Java) involves having the C code actually create a JVM.
To answer your question about the code sample, the Java Strings being created are returned with the return statement at the end of code execution, logically, this is when program flow for that thread of execution is returned back to the JVM.
I have a Java app which needs to interact with the camera on a Windows Mobile device. I have written the Java Code and the Native code and it all works fine. The problem I am having now is that I want to start passing variables from Java to the Native code, e.g. the directory and file name to use for the photo.
The native code uses a SHCAMERACAPTURE object to interact with the camera and it expects the directory and filename to be specified using LPCTSTRs. The string passed in is a jstring, which I can get to a const char * by calling:
const char *strDir=(jEnv)->GetStringUTFChars(dirName, 0);
But I am not sure how I can pass this to the the SHCAMERACAPTURE object because it cannot convert const char * to LPCTSTR. I tried a cast (LPCTSTR)strDir and it compiled, but I get an error when it runs (that it can't create the file).
I am a Java developer and pretty new to C++ etc. so I am really not too sure what I need to do to get my string into the native call. Any ideas?
I think you should try GetStringChars() instead of GetStringUTFChars()
According to this page it returns the Unicode String.
WindowsCE and Windows mobile use UNICODE exclusively so LPCTSTR
is actually LPCWSTR (Long Pointer to Const WideChar String)
SHCAMERACAPTURE shcc;
ZeroMemory(&shcc, sizeof(shcc));
shcc.cbSize = sizeof(shcc);
shcc.pszInitialDir = (TCHAR*)(jEnv)->GetStringChars(dirName, 0 );
shcc.pszDefaultFileName = (TCHAR*)(jEnv)->GetStringChars(defFileName, 0 );
I assume you want to provide a path and a filename. This is adapted from this MS page