I have jobject and its has jobjectArray inside ,I am using GetObjectField to get jobjectArray and then casting it , but its not working can anyone help ?
jfieldID representationArrayF = env->GetFieldID(className, "representationArray", "[Lcodec/video/initial/MMRepresentation");
jobject objectArr=(jobject)env->GetObjectField(jpresentationInfo, representationArrayF);
jobjectArray* objectArray=reinterpret_cast<jobjectArray*>(&objectArr);
jobject representation=(jobject) env->GetObjectArrayElement(*objectArray, i)
it is silly mistake which is fine with simple parameters but with array parameters it not working instead of [Lcodec/video/initial/MMRepresentation , i have to put [Lcodec/video/initial/MMRepresentation; semicolon at end
don't know with simple parameters like III semicolon is not necessary
Related
I need to call some java code from C++ using JNI.
I can't figure out how to get a value from a returned java generic with JNI. Java code that I need to call from C++ is:
encoderCapabilities.getQualityRange().getLower()
The problem is java returns the generic type Range<Integer>:
public Range<Integer> getQualityRange ()
I tried to use following C++ code, but it crash:
GetMethodID and CallObjectMethodV with function name getQualityRange and arguments ()Landroid/util/Range;. It seems did not crash, but next call crash:
getLower, ()I
Could you please suggest what code can work?
The object Range is fine as its method toString returns valid string "[1,100]", but "getLower" failed on getting methods.
Upd: The Answer from Botje works!
After type erasure, Range#getLower will have declared type Comparable, regardless of what the type in the Java source was.
Try this instead:
jobject range = ...;
jclass cls_Range = env->GetObjectClass(range);
jmethodID mid_Range_getLower = env->GetMethodID(cls_Range, "getLower", "()Ljava/lang/Comparable;");
jobject lower = env->CallObjectMethod(range, mid_Range_getLower);
jclass cls_Integer = env->GetObjectClass(lower);
jmethodID mid_Integer_intVale = env->GetMethodID(cls_Integer, "intValue", "()I");
jint lowerInt = env->CallIntMethod(lower, mid_Integer_intValue);
JNIEXPORT jstring JNICALL Java_com_weiss_konrad_arrowapp_NDKInterface_getID(JNIEnv *env, jobject o ){
Mat img = imread("storage/emulated/0/DCIM/Camera/IMG_20160506_141333.jpg");
if(img.empty())
{
return env->NewStringUTF("Image not loaded");
}
return env->NewStringUTF("Image loaded");
}
Different devices may use different paths to store the camera pictures. You must use Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) to find the correct path (sometimes it can even be changed by the end-user). I understand that it is a burden to find call this function from C++, so probably you will find it easier to pass this path from Java.
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'm trying to wrap a c++ library for a specific usb device in JAVA.
the library supports Callback functions to inform the application about the attachment and detachment of usb device to PC.
the call back function must have a specific format like this:
DWORD callbackFunction(void *params);
so i implemented a function like this in JNI dll and want to call a function in Java wapper whenever this function is called.
the question is what JNIENV i should use for calling GetObjectClass, GetMethodID and CallVoidMethod from?
This is how I initialize my DLL. the "Set(AttachDetach)Callback" methods accept a callback function(first parameter) and a void* parameter(secondparameter) that will be passed to the function when module attach/detach is detected.
JNIEXPORT void JNICALL Java_MyPackage_MyClass_InitializeDLL
(JNIEnv *env, jobject obj, jobject callback)
{
// Storing callback object in global variable.
callBackObj = callback;
env->GetJavaVM(&jvm);
MyInstance = new MyClass();
MyInstance ->SetAttachCallback(AttachCallBack, &callBackObj);
MyInstance ->SetDetachCallback(DetachCallBack, &callBackObj);
// Testing!
jclass callBackCls = env->FindClass("MyPackage/MyClassCallBacks");
jmethodID mid = env->GetMethodID(callBackCls, "attach", "(B)V");
if (!mid)
return ; /* method not found */
//This call here works well
env->CallVoidMethod(callBackObj, mid, 5);
}
then i set a callback function in DLL for the USB device and it is successfully called when i attach the device.
The code i put in attach callback of the USB device is this:
DWORD CALLBACK AttachCallBack(CallbackParams* params)
{
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
jclass callBackCls = env->FindClass("MyPackage/MyClassCallBacks");
jmethodID mid = env->GetMethodID(callBackCls, "attach", "(B)V");
if (!mid)
return -1; /* method not found */
// This call fails with an access violation Exception
env->CallVoidMethod(*((jobject *)(params->param)), mid, params->moduleIndex);
// This fails the same way too
env->CallVoidMethod(callBackObj, mid, 5);
jvm->DetachCurrentThread();
return 0;
}
Before i use AttachCurrentThread i wasn't able to use JNIENV pointer at all. but now any other use of this pointer is successful instead of the call to CallVoidMethod.
Do you see what is wrong here?
Let me add that MyPackage.MyClassCallBacks Is an interface that it's method is implemented in another calss namely "callBackClass"
You need to have a reference to the current JVM:
JavaVM *jvm;
You can add an initilizing method to the C++ backend, which obtains this reference when the program starts:
JNIEXPORT void JNICALL init(JNIEnv *env, jclass){
env->GetJavaVM(&jvm);
}
And when observing for USB attachemnt / detachment, you can obtain JNIEnv from this JavaVM like this:
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
//your code here
jvm->DetachCurrentThread();
This is implemented so that every USB device change creates a new thread. If you use only one thread for the chceking, you need to attach only once (in the initilizer, perhaps?) and you will then have JNIEnv valid as long as your native thread stays attached to the JVM.
What you may have to do is create a queue in C and wait on it or poll it using a Java thread. This would always have a current JNIEnv available to it.
It seems you can't ...
.... save the JNIENV from the last JNI call which set this up and reuse it.
Your callback appears to return params which you may have passed it when you setup the call back. You can make one of them the JNIENV.
I had the same problem too. It was as though the reference to the object, created in the initialization method, was of no use in the other methods. And it is indeed like that.The solution is in the initialization of the reference to the object, which must be initialized not simply with
callBackObj = callback
but with
callbackObj = env->NewGlobalRef(callback)
Same issue here: Objective C calling Java methods using JNI
Create a JNI init(JNIEnv * env, jclass c (or jobject o) ) and
save param #1 JNIEnv
save param #2 jclass (if static)
or
save param #2 jobject (in non-static)
lookup and save the jmethodID(s) for the Java method(s) you will be invoking.
Good idea to also have a JNI shutdown(JNIEnv * env, jclass (or jobject) ) for native shutdown/cleanup
I'm trying to pass an image (PNG file that it is only in memory and not on the hard-disk) from an old C++ application to my Java 8 application, but I'm not finding solutions. From my Java app, I call native functions in the C++ DLL.
In my Java app I suppose I should write something that works with an
array of byte.
public static native Byte[] getByteArrayImage()
In the C++ app I wrote this code
JNIEXPORT jbyteArray JNICALL Java_it_getByteArrayImage(JNIEnv * env, jclass obj, jint imagekind)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
unsigned long bufferSize;
BYTE* buffer = PaintToBufferImage(imagekind, &bufferSize);
jbyteArray jb= env->NewByteArray(bufferSize);
env->SetByteArrayRegion(jb, 0, bufferSize, (jbyte *)buffer);
return jb;
}
Could you help me? Thank you.