On Android platform in my native code I have allocated an int array
mBuffer = new int[BUFSIZE];
I want to send this to Java side, Java method is this
public void WriteBuffer(int[] buffer, int size)
{
}
I call back to java code like this
const char* callback = "WriteBuffer";
mWriteMethod = env->GetMethodID(cls, callback, "([II)V");
This calls the java method its just that in my Java code the buffer is null. As I am really passing a pointer to memory that was dynamically allocated rather than an actual array is probably why it doesn't work but I don't know how to pass a pointer to Java.
I need the buffer parameter as an integer array on the Java side anyway.
Anyone know how I can modify the above to get it to work?
Thanks
My understanding of your question is you want call the java method WriteBuffer and pass a int[] to it.
Some pseudo-code which you will need in jni
jintArray buffer;
buffer= (*env)->NewIntArray(env, BUFSIZE);
(*env)->SetIntArrayRegion(env, buffer, 0,BUFSIZE, mBuffer);
SetIntArrayRegion() will copy from mBuffer into the Java array.
Related
I have an image buffer allocated on the heap in my c++ code that I would like to share with some other c++ objects as well as Java objects through JNI. On the native side im using shared_ptr and I was wondering what is the best way to do so ? my thought is to allocate the buffer on the heap once and share a reference everywhere. I'm taking advantage of smart pointers so that the buffer will be deallocated as soon as all the references go out of scope, but I'm facing an issue when sharing a reference to the java side.
how can I ensure that my java object has a valid reference to the buffer all the time ? how can c++ determine that the reference counter reaches 0 when java object is done using its reference. My concern is to avoid memory leak and also ensure that buffer doesn't get destroyed too soon before getting processed by the java class.
thanks for the help
The general answer is "make the Java object's lifetime influence the lifetime of the C++ object".
Start with the following Java class:
class Refholder implements AutoCloseable {
private long ptr; // the actual pointer
private long shared_ptr; // a pointer to a shared_ptr keeping `ptr` alive
public Refholder(long ptr, long shared_ptr) {
this.ptr = ptr;
this.shared_ptr = shared_ptr;
}
public native void close();
public void finalize() { close(); }
// Other methods to access the contents of `ptr` go here.
};
This will contain both the actual pointer and a pointer to a shared_ptr.
When you want to hand a reference to Java, use the following:
jobject passToJava(JNIEnv *env, std::shared_ptr<Foo> instance) {
jclass cls_Refholder = env->FindClass("Refholder");
jmethodID ctr_Refholder = env->GetMethodID(cls_Refholder, "<init>", "(JJ)V");
// This line increases the reference count and remembers where we put the copy
std::shared_ptr<Foo> *copy = new std::shared_ptr<Foo>(std::move(instance));
jobject ret = env->NewObject(cls_Refholder, ctr_Refholder, copy->get(), copy);
return ret;
}
Finally, the close method is responsible for extracting the shared_ptr and deallocating it:
JNIEXPORT void Java_Refholder_close(JNIEnv *env, jobject obj) {
jclass cls_Refholder = env->GetObjectClass(obj);
jfieldID fld_Refholder_ptr = env->GetFieldID(cls_Refholder, "ptr", "J");
jfieldID fld_Refholder_shared_ptr = env->GetFieldID(cls_Refholder, "shared_ptr", "J");
std::shared_ptr<Foo> *copy = (std::shared_ptr<Foo>*)env->GetLongField(obj, fld_Refholder_shared_ptr);
if (!copy)
return;
env->SetLongField(obj, fld_Refholder_ptr, 0);
env->SetLongField(obj, fld_Refholder_shared_ptr, 0);
delete copy;
}
I have decided to implement both AutoCloseable and finalize because I do not know how your Java code plans to use the reference. If you need deterministic destruction of the C++ object you need to use try-with-resources or explicit calls to the close method. If you do not, at last the finalize will close it.
How can I write my c++ JNI function so that it returns an array of Mat to Java code?
I am programming in Android environment, with the help of NDK to use also some functions of OpenCV.
My c++ function is:
JNIEXPORT void JNICALL Java_com_micaela_myapp_MainActivity2_getFrames(JNIEnv* env, jobject object, jstring path)
{
const char *str;
str = env->GetStringUTFChars(path, NULL);
VideoCapture input_video;
if(input_video.open(str)){
cout<<"Video File Opened"<<endl;
}else{
cout<<"Video File Not Found"<<endl;
}
Mat image;
Mat frameBuffer[1000];
int i=0;
while(input_video.read(image)==true){
image.copyTo(frameBuffer[i]);
i++;
}
}
In Java I have:
static{
System.loadLibrary("myapp");
}
public static native void getFrames(String path);
This function now returns void and works properly. However, my purpose is to obtain the array frameBuffer from it, in order to use it in Java. How can I do this?
One solution is to allocate an array of equal size in Java, pass it to your native getFrames() function, and inflate the Mat objects individually using your frame buffer. Please see this post for an example of passing an array to native code, and this one for a way to inflate a Java Mat from a native one.
If you really need to create the array in native code and return it, please have a look at the NewObjectArray method that's available through JNI. (Example)
I am working on a jni-client-software, which should communicate with a server. I can establish the connection, can read out the information I need and give it back to my java programm. Now I want to to the connection infinite, that means the connection is established and the information should be read out in a infinite loop (I don't want to disconnect and reconnect with every jni-function call). Is it possible to pass a byte array from the working jni tread to a my java programm?
Thank you very much.
Kind regards
Thomas
"Is it possible to pass a byte array from the working jni tread to a my java programm?"
you can create static method in one of your java classes, and then call this method with parameters from within jni code. Here is some code:
java side:
package com.mysuper.game;
public class MyApp {
public static void callMeFromJNI(byte[] data) {
// ...
}
}
and c++ code run on worker thread :
JavaVM *vm;
// use vm->AttachCurrentThread(&env, 0); in thread function to get valid JNI interface pointer, on thread end use DetachCurrentThread().
JNIEnv *env;
void myFunc() {
// some test data to send
const int len = 32;
char data[len] = {0,1,2,3,4};
jclass app = env->FindClass("com/mysuper/game/MyApp");
jmethodID sendDataToJava = env->GetStaticMethodID(app, "callMeFromJNI", "([B)V");
jbyteArray bArray = env->NewByteArray(len);
char *bytes = (char *)env->GetByteArrayElements(bArray, 0);
memcpy( bytes, data, len);
env->ReleaseByteArrayElements(bArray, bytes, JNI_ABORT);
env->CallStaticVoidMethod(app, sendDataToJava, bArray);
}
for more on how this works look into:
Java Native Interface 6.0 Specification
I want to access an array that is created and updated in the native C code efficiently. If need be, i could send a pointer or reference from Java code to the native side and have the C-code populate it so that i can read it from SDK side when it's ready to be consumed.
Currently, this is how i am doing it. But i think there can be better ways to do it, since i am doing one copy in C-side and then there an object that is created every time i issue a read on the Java-side.
My Java code:
double[] valuesFromNative = getValues();
public static native double[] getValues();
static { System.loadLibrary("test-jni"); }
My native (C and not C++) code:
#define LEN 18
double testDoubleArr[LEN];
jdoubleArray Java_com_test_testActivity_getValues(JNIEnv *env, jclass clazz) {
jboolean isCopy;
int i;
jdoubleArray result = (*env)->NewDoubleArray(env, LEN);
jdouble* destArrayElems = (*env)->GetDoubleArrayElements(env, result, &isCopy);
for (i = 0; i < LEN; i++) {
destArrayElems[i] = testDoubleArr[i];
}
if(isCopy == JNI_TRUE) {
// isCopy should NEVER be JNI_TRUE in this case, right?
// so, i could as well replace this condition with
// assert(isCopy == JNI_FALSE)?
}
return result;
}
This code snippet works - so, i am looking at more efficient or rather correct way to achieve the same thing.
Thanks for sharing your thoughts.
I think SetDoubleArrayRegion() would be faster. Less code and less JNI calls, that's for sure.
jdoubleArray result = (*env)->NewDoubleArray(env, LEN);
(*env)->SetDoubleArrayRegion(env, result, 0, LEN, testDoubleArr);
You don't even have to create the array on the C++ side. Declare the method like this:
public static native void getValues(double[] a);
Implement like this:
void Java_com_test_testActivity_getValues(JNIEnv *env, jclass clazz, jdoubleArray a)
{//...
Create the array on the Java side, cache it in an instance variable or something, and pass it to JNI to be filled whenever needed. Make sure the assumptions about array size are the same on the Java side and on the C side.
I want to pass a ByteBuffer over JNI to C++, as the buffer to receive an image decoded from AVDecode, though the buffer is correctly filled in C++, but the ByteBuffer at the Java side is still empty.
Please help me to find out where is the error. Thanks.
pOutBuffer is the ByteBuffer passed via JNI.
jclass ByteBufferClass = env->GetObjectClass(pOutBuffer);
jmethodID ArraryMethodId = env->GetMethodID(ByteBufferClass,"array","()[B");
jmethodID ClearMethodId = env->GetMethodID(ByteBufferClass,"clear","()Ljava/nio/Buffer;");
//clear buffer
env->CallObjectMethod(pOutBuffer,ClearMethodId);
jbyteArray OutByteArrary = (jbyteArray)env->CallObjectMethod(pOutBuffer,ArraryMethodId);
jbyte OutJbyte = env->GetByteArrayElements(OutByteArrary,0);
Out = (unsigned char*)OutJbyte;
DecodeSize = AVDecode(m_pVideoDecode, (unsigned char *)In, inputSize, (unsigned char **)&Out, (int *)&pBFrameKey);
The decoding is correct and I can see that 'Out' is filled with the output image, however, when this function returns, the pOutBuffer at the Java side is still empty.
How was the ByteBuffer created? Is it a direct or non-direct ByteBuffer?
If it's a direct ByteBuffer which has been created in Java using the allocateDirect method you can us GetDirectBufferAddress in your native code to get the direct address of the ByteBuffer and any changes there should be reflected in Java.