I have a java method, like this:
public static native void receiveCallback(byte[] value1, byte[] value2);
In JNI I can reach the class and I can reach the method, but my parameter list is incorrect. I am trying to call the method like this:
jmethodID testJavaMethod = (java_environment)->GetMethodID(clazz, "receiveCallback","([B[B");
I then get a NoSuchMethod exception at runtime.
I followed multiple SO questions, including this one JNI - How to callback from C++ or C to Java?, but I'm still stuck.
Any suggestions?
Thanks.
If you want to call back into Java, the implementation must be in Java. You need
public static void receiveCallback(byte[] value1, byte[] value2) {
// do something with value1 and value2
}
And to get the id of a static method you need to use "GetStaticMethodID". Also to call it you will have to use "CallStaticVoidMethod()".
Related
I was recently reading a java class where the person had created methods via IDE shortcut( extract method shortcut ). All methods had a return type of void and what was supposed to be return type was passed as input parameter for the method. Below is an example of what i'm referring to.
public void multiply(Integer first, Integer second, Integer result){
result = first * second;
}
I was wondering if the above is a good way of doing things. I personally do not like the above way of creating methods. I think the method should have been like below.
public Integer multiply(Integer first, Integer second, Integer result){
return first * second;
}
But in IntelliJ/Eclipse when we do extract method mostly creates method like above.
Can someone please let me know why IDE's usually create void method when we use the extract method shortcut and is the 1st style of method definition a good practice at all?
If the method being called isn't being assigned to a variable, Eclipse has no way to know what the return value is supposed to be.
Presumably, the original code looked something like this:
public static void main(String args[]){
Integer result = 0;
multiply(1,3,result);
}
There's no way for Eclipse to divine that multiply is supposed to return anything, so it defaults to void. If you want to infer return values, have it be assigned to a variable like so:
public static void main(String args[]){
Integer result = 0;
result = multiply(1,3,result);
}
Short Question:
Passing a parameter from C++/C JNI to Java method who supposed to modify. Will the modified result available back in C/C++ JNI? Is there any way to do it? Other than return and a callback from java?
Continue if above is not clear:
Calling below function from C/C++ JNI with jcharArray. Below Java function is supposed to work on passed char array and store the final result in same. So that modified result will be available back in C/C++ JNI.
I have java function accepting a char[] like below
void GetName(char[] s)
{
String t = "Test";
// Work on t and store the result in s
s = t.toCharArray();
}
How I can achieve this ... I am getting all the examples that are another way around like Java calling C/C++.
Java only supports pass by value. If you want to return a reference you need to either
a) return it
char[] getName();
b) Pass a mutable object to reference it
void getName(char[][] nameArray) {
nameArray[0] = t.toCharArray();
}
c) Pass a call back
void getName(Consumer<char[]> listener) {
listener.accept(t.toCharArray());
}
So for my scenario, I modified the declaration like below
void GetName(char[] s) to void GetName(PersonName obj)
So now, I am creating an object of PersonName in JNI and passing that as a parameter to the java function. Later I am using the same object to call getter methods of the PersonName class to get the values char[] RetriveName();
I also found that using jfield also you can get the values of the class data members but then those members should be public
I'm basically trying to create a static method that will serve as a wrapper for any method I pass and will execute something before and after the actual execution of the method itself. I'd prefer to do it using Java 8 new coding style. So far I have a class that has a static method, but I'm not sure what the parameter type should be so it can take any method with any type of parameter and then execute it. Like i mentioned I want to do some stuff before and after the method executes.
For example: executeAndProcess(anyMethod(anyParam));
Your method can accept a Supplier instance and return its result:
static <T> T executeAndProcess(Supplier<T> s) {
preExecute();
T result = s.get();
postExecute();
return result;
}
Call it like this:
AnyClass result = executeAndProcess(() -> anyMethod(anyParam));
I am trying to call a method defined in android activity in c++ qt using QAndroidJniObject.
here is my call in c++ class
QAndroidJniObject data = QAndroidJniObject::callStaticObjectMethod("com/android/app/appActivity",
"appData",
"(I)Ljava/lang/String;");
QString dataValue = data.toString();
qDebug() <<"Data is " << dataValue;
this appData is defined in appActiviy android class and it returns a String
this is defined method I want to call and get the returned string value
static String appData(){
Log.d("App Data is ", "Working");
return data;
}
but I am getting null is dataValue and it is not throwing any error too.
You may need to manually check exceptions to get your Java errors.
From Qt documentation:
Handling Java Exception
When calling Java functions that might throw an exception, it is important that you check, handle and clear out the exception before continuing.
Note: It is unsafe to make a JNI call when there are exceptions pending.
void functionException()
{
QAndroidJniObject myString = QAndroidJniObject::fromString("Hello");
jchar c = myString.callMethod<jchar>("charAt", "(I)C", 1000);
QAndroidJniEnvironment env;
if (env->ExceptionCheck()) {
// Handle exception here.
env->ExceptionClear();
}
}
Are you sure you want to be calling com/android/app/appActivity and not com/android/app/Activity?
Here are some thoughts:
Have you used Log.d() to print the string before return making sure it is not null?
Not sure it matters, but you are specifying an integer as argument, but the Java method does not have that in its signature. You should then provide this integer as a parameter in callStaticObjectMethod().
As mentioned by Alex P, exceptions have to be handled or they will give you a headache as they might happen quite often and crash the entire application.
I can not find any class at com/android/app/appActivity in the Android documentation. Did you mean com/android/app/Activity? If so, I can't find a method named "appData" here.
Thanks guys for your answer, finally I figured it out. It was way simple then I was trying
QAndroidJniObject data = QAndroidJniObject::callStaticObjectMethod("com/android/app/appActivity",
"appData",
"(I)Ljava/lang/String;");
In this code I was not aware that this(I)Ljava/lang/String; means type of parameter your Java method is accepting but in my case there were none. So the correct answer is
QAndroidJniObject data = QAndroidJniObject::callStaticObjectMethod<jstring>("com/android/app/appActivity",
"appData")`
denotes return type of my defined java method.
I guess it was silly mistake from my end...thanks again
I have the same problem today. Although it is different from your code, but the return value is also NULL.here is my code:
package org.test.project.test;
public class TestJava {
public static String notify(int iNumber )
{
String strNum = iNumber+"";
return strNum;
}
}
and then the c++ code:
QAndroidJniObject str = QAndroidJniObject::callStaticObjectMethod("org/test/project/test/TestJava",
"notify",
"(I)Ljava/lang/String;",
m_iNumber);
//the str always been NULL
I have a C API that looks like this:
int my_function(char** assign_me_a_string);
I basically need the native code to tell me a value. If I do:
char* my_function();
It works fine. I get a String in return that I can use in Java. However I would prefer to use the first approach since all my functions returns an int by default (status value).
I have tried to use various.i and this typemap:
%apply char **STRING_ARRAY { char **assign_me_a_string }
Doing this I get a String[] generated for the Java API. Then I try to use it by:
String[] myStringToAssign = new String[1];
my_function(myStringToAssign);
But this seems to just crash.
So, is there a proper way to assign a value to a Java String from inside the C code? I am not trying to use an array, I just need to be able to dereference the char** and assign it a string in the native code that can then be used as a String object in Java.
Java String is immutable and references are passed by value so the following will not do what you think (pure Java):
// java:
void someFunction(String yourString) {yourString = "bye";}
void test() {
String test = "hi";
someFunction(test);
System.out.println(test); // prints "hi", not "bye"!
}
(For more on this, see for example Passing a String by Reference in Java?). Needless to say you can't do it using JNI either.
Look at section 24.10.5 of SWIG 2.0 docs: it shows how you could do this for char** via a typemap. However the typemap there would have to be modified to check the char* array on exit (the freearg typemap, perhaps) to replace the contents of the String[]. Your function could assume only one item.
Alternately you could wrap your Java String in a class, which will get passed by reference; however you again would have to use a typemap to copy any changes that have been made into the data member.
Probably the easiest is to provide a helper function that swaps the status code and string:
// C/C++ lib: the function you'd like to export but can't:
int my_function(char*& ) {
val = new char[20];
...put stuff in val, don't forget the terminating \0 char...
return status;
}
Create wrapper:
// SWIG .i file: an "adapter" function, Java gives you no choice:
%inline %{
char* my_function(int& err) {
char * val;
err = my_function(val);
return val;
%}
SWIG will take care of copying the char* to the returned String:
// From Java you can then do:
int status;
String result = my_function(status);
System.out.println(result);
No, Java Strings are immutable and cannot be assigned not even through JNI.