JNI: How to convert a group of data from c++ to Java - c++

I'm trying to send some data from c++ to java using JNI.
In c++ I have:
Array[0]:
string name = "myName"
int iterations = 16
float value = 15
... etc
So I want to use JNI to return all data on Java, I'm trying this, but don't work
JNIEXPORT jobjectArray JNICALL Java_com_testing_data_MainActivity_getDATA(JNIEnv *env, jobject obj)
{
// 1º Create a temp object
jobject dataClass
{
jstring name;
jint iterations;
jfloat value;
};
jobject tempObject = env->NewObject();
// Get data in c++ format int temp object type
std::vector<dataClass > data = getDataClass(); // First error, must be a c++ class, how could i get it?
// How much memory i need?
int dataSize = data.size();
// Reserve memory in java format
jint tempValues[dataSize];
jobjectArray tempArray = env->NewObjectArray(dataSize,dataClass,0); // 2º Error, it doesn 't create the class
// Temporal store data in jarray
for (int i = 0; i < dataSize ; i++)
{
tempArray[i].name = data[i].name;
tempArray[i].iterations = data[i].iterations;
tempArray[i].value = data[i].value;
}
return tempArray; // return temp array
}
Are correct this steps to return a structure/object with data? How is possible to fix the errors?

Converting everything to JNI types is not a good idea. Generally, it is better to create a peer object, i.e. a handle pointer to the native resource - like the hWnd in the Windows GUI Programming.

You can use a string to store all data as a sequence data. Field are separated by a separator (":" for example), like this:
std::string sequenceData = "my name" + ":" + "16" + ":" + "15" + ...;
Pass this sequence to java then split it to get desired value. Use String.split() or StringTokenizer.

Related

How can I import Array<Mat> to JNI from Kotlin?

I am new to coding JNI,
So my problem was when I debugging my Android Studio, it gives me this "error: call to implicitly-deleted copy constructor of 'cv::Mat'"
And I don't know why. I guess it's because of how I try to change from 'jlong' and 'jlongArray' to 'Mat'. My native-lib.cpp was:
JNIEXPORT jlong JNICALL
Java_com_android_example_panoramacamera_fragments_CameraFragment_imagesPass(JNIEnv *env,
jobject thiz, jlongArray image_in_,
jlong image_out_) {
// TODO: implement imagesPass()
Stitcher::Mode mode = Stitcher::PANORAMA;
Mat *image_in = (Mat*) image_in_, *image_out = (Mat*) image_out_;
// Create a Stitcher class object with mode panoroma
Ptr<Stitcher> stitcher = Stitcher::create(mode, false);
// Command to stitch all the images present in the image array
Stitcher::Status status = stitcher->stitch(*image_in, *image_out);
if(status == Stitcher::OK){
return (jlong) image_out;
}
}
And my kotlin-sript:
private external fun imagesPass(imageIn: LongArray, imageOut: Long): Long
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == pickImageCode && resultCode == RESULT_OK) {
if (data != null) {
if (data.clipData != null) {
val count = data.clipData!!.itemCount
for (i in 0 until count) {
val imageUri = data.clipData!!.getItemAt(i).uri
val imageStream: InputStream? = context?.contentResolver?.openInputStream(imageUri)
val bitmap = BitmapFactory.decodeStream(imageStream)
val mat = Mat()
Utils.bitmapToMat(bitmap, mat)
imagesMat[i] = mat
}
}
else {
val imageUri = data.data
val imageStream: InputStream? = imageUri?.let { context?.contentResolver?.openInputStream(it) }
val bitmap = BitmapFactory.decodeStream(imageStream)
val mat = Mat()
Utils.bitmapToMat(bitmap, mat)
imagesMat[0] = mat
}
}
for(i in imagesMat.indices){
longArray[i] = imagesMat[i].nativeObj
}
long = imagesPass(longArray, imageStitch.nativeObj)
imageStitch = Mat(long)
}
super.onActivityResult(requestCode, resultCode, data)
}
and as you can see, I have tried to import Mat but since jni.h is so limited in its language, I have to convert my Mat to Long so I can use both Array and Mat.
But then my "opencv2/core/mat.inl.hpp" start to show error:
inline Mat _InputArray::getMat(int i) const
{
if( kind() == MAT && i < 0 )
return *(const Mat*)obj; //This line gets error
return getMat_(i);
}
So my question is how can I convert from jlongArray to InputArray? or How can I import Array to JNI from Kotlin?
Thank you very much.
Doing something like
Mat *image_in = (Mat*) image_in_
is incorrect code. For all practical purposes, always treat all JNI objects as opaque objects, making no assumption as to how they store the actual underlying data and instead use the JNI APIs to manipulate these objects, including retrieving the actual data from them. A jlongArray is not equivalent to something like jlong array[] = {1, 2, 3}.
From what I understand, you need access to the underlying native elements from the Java jlongArray. There are 2 possible options:
Get the backing native elements using GetLongArrayElements(). This provides a native array of jlongs which is valid until ReleaseLongArrayElements() is called.
Create a copy of a range of elements from the jlongArray using GetLongArrayRegion() that provides a copy of the buffer into a jlong* buffer. This buffer's life is not tied to the actual jlongArray. If the elements from this buffer need to be copied back to the original jlongArray then SetLongArrayRegion() can be used.
Once you have access to the native buffers, then they can be used in C++ code as usual like an array of longs.
An example for a solution with the 1st approach would look something like:
Java_com_android_example_panoramacamera_fragments_CameraFragment_imagesPass
(JNIEnv * env, jobject thiz, jlongArray imageIn, jlong ImageOut) {
jsize len = env->GetArrayLength(imageIn);
jlong * nativeImageList = env->GetLongArrayElements(imageIn, NULL);
//Now one can do something like
//Mat* image_in = reinterpret_cast<Mat*>(nativeImageList);
//This should give the native version of images
for(jsize idx = 0; idx < len; idx++) {
std::printf("%zd ", nativeImageList[idx]);
}
std::printf("\n");
//Once done with the array, release it back to JVM
env->ReleaseLongArrayElements(imageIn, nativeImageList, 0);
return ImageOut;
}
In the above piece of code, the nativeImageList is an array of jlong which is equivalent to what was passed in from the Java/Kt layer into longArray. Each of the elements in this nativeImageList will be the same as what was stored with the line
longArray[i] = imagesMat[i].nativeObj
Hence nativeImageList[0] shall be the value of imagesMat[0].nativeObj and so on. This is obviously a handle to the underlying image and can be just used as
Mat* image_in = reinterpret_cast<Mat*>(nativeImageList);
Note the difference from
Mat* image_in = (Mat*) image_in_
Here, the native elements are retrieved and then cast into Mat*, not directly from the jlongArray object.
Reference

returning a char array from android NDK [duplicate]

I am attempting to use the android NDK.
Is there a way to return an array (in my case an int[]) created in JNI to Java? If so, please provide a quick example of the JNI function that would do this.
-Thanks
If you've examined the documentation and still have questions that should be part of your initial question. In this case, the JNI function in the example creates a number of arrays. The outer array is comprised of an 'Object' array creating with the JNI function NewObjectArray(). From the perspective of JNI, that's all a two dimensional array is, an object array containing a number of other inner arrays.
The following for loop creates the inner arrays which are of type int[] using the JNI function NewIntArray(). If you just wanted to return a single dimensional array of ints, then the NewIntArray() function is what you'd use to create the return value. If you wanted to create a single dimensional array of Strings then you'd use the NewObjectArray() function but with a different parameter for the class.
Since you want to return an int array, then your code is going to look something like this:
JNIEXPORT jintArray JNICALL Java_ArrayTest_initIntArray(JNIEnv *env, jclass cls, int size)
{
jintArray result;
result = (*env)->NewIntArray(env, size);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
int i;
// fill a temp structure to use to populate the java int array
jint fill[size];
for (i = 0; i < size; i++) {
fill[i] = 0; // put whatever logic you want to populate the values here.
}
// move from the temp structure to the java structure
(*env)->SetIntArrayRegion(env, result, 0, size, fill);
return result;
}
if someone would like to know how to return String[] array:
java code
private native String[] data();
native export
JNIEXPORT jobjectArray JNICALL Java_example_data() (JNIEnv *, jobject);
native code
JNIEXPORT jobjectArray JNICALL
Java_example_data
(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);
}
from link:
http://www.coderanch.com/t/326467/java/java/Returning-String-array-program-Java
Based on the asked question, this is already explained in the first answer that how can we pass int[] via jobjectArray. But Here is an example how we can return a jobjectArray which contains lists of data. This can be helpful for situations for example: when someone needs to return data in 2D format to draw some line with x and y points. The below example shows how a jobjectArray can return data in the form of following format:
Java input to the JNI:
Array[Arraylist of x float points][Arraylist of y float points]
JNI output to java:
jobjectArray[Arraylist of x float points] [Arraylist of y float points]
extern "C" JNIEXPORT jobjectArray JNICALL
_MainActivity_callOpenCVFn(
JNIEnv *env, jobject /* this */,
jobjectArray list) {
//Finding arrayList class and float class(2 lists , one x and another is y)
static jclass arrayListCls = static_cast<jclass>(env->NewGlobalRef(env->FindClass("java/util/ArrayList")));
jclass floatCls = env->FindClass("java/lang/Float");
//env initialization of list object and float
static jmethodID listConstructor = env->GetMethodID(arrayListCls, "<init>", "(I)V");
jmethodID alGetId = env->GetMethodID(arrayListCls, "get", "(I)Ljava/lang/Object;");
jmethodID alSizeId = env->GetMethodID(arrayListCls, "size", "()I");
static jmethodID addElementToList = env->GetMethodID(arrayListCls, "add", "(Ljava/lang/Object;)Z");
jmethodID floatConstructor = env->GetMethodID( floatCls, "<init>", "(F)V");
jmethodID floatId = env->GetMethodID(floatCls,"floatValue", "()F");
//null check(if null then return)
if (arrayListCls == nullptr || floatCls == nullptr) {
return 0;
}
// Get the value of each Float list object in the array
jsize length = env->GetArrayLength(list);
//If empty
if (length < 1) {
env->DeleteLocalRef(arrayListCls);
env->DeleteLocalRef(floatCls);
return 0;
}
// Creating an output jObjectArray
jobjectArray outJNIArray = env->NewObjectArray(length, arrayListCls, 0);
//taking list of X and Y points object at the time of return
jobject xPoint,yPoint,xReturnObject,yReturnObject;
//getting the xList,yList object from the array
jobject xObjFloatList = env->GetObjectArrayElement(list, 0);
jobject yObjFloatList = env->GetObjectArrayElement(list, 1);
// number of elements present in the array object
int xPointCounts = static_cast<int>(env->CallIntMethod(xObjFloatList, alSizeId));
static jfloat xReturn, yReturn;
jobject xReturnArrayList = env->NewObject(arrayListCls,listConstructor,0);
jobject yReturnArrayList = env->NewObject(arrayListCls,listConstructor,0);
for (int j = 0; j < xPointCounts; j++) {
//Getting the x points from the x object list in the array
xPoint = env->CallObjectMethod(xObjFloatList, alGetId, j);
//Getting the y points from the y object list in the array
yPoint = env->CallObjectMethod(yObjFloatList, alGetId, j);
//Returning jobjectArray(Here I am returning the same x and points I am receiving from java side, just to show how to make the returning `jobjectArray`)
//float x and y values
xReturn =static_cast<jfloat >(env->CallFloatMethod(xPoint, floatId,j));
yReturn =static_cast<jfloat >(env->CallFloatMethod(yPoint, floatId,j));
xReturnObject = env->NewObject(floatCls,floatConstructor,xReturn);
yReturnObject = env->NewObject(floatCls,floatConstructor,yReturn);
env->CallBooleanMethod(xReturnArrayList,addElementToList,xReturnObject);
env->CallBooleanMethod(yReturnArrayList,addElementToList,yReturnObject);
env->SetObjectArrayElement(outJNIArray,0,xReturnArrayList);
env->SetObjectArrayElement(outJNIArray,1,yReturnArrayList);
__android_log_print(ANDROID_LOG_ERROR, "List of X and Y are saved in the array","%d", 3);
}
return outJNIArray;
Simple solution is that write the array data in a file from C,and then access the file from Java

JNI date value conversion issue, getting a different long value in C++

I'm badly stuck in one basic date conversion in jni as the (long) values passed from Java to JNI/C++ are quite different.
My Java date objects are initialized in class as,
Date date1=new Date(1220227200L * 1000);
// If I convert into milliseconds such as
long timeInMillisecond=date1.getTime();
System.out.println (timeInMillisecond )
// it obviously displays 1220227200000
In my native function, i'm retrieving as
jfieldID fid = env->GetFieldID(cls, "date1", "Ljava/util/Date");
// case 1 - value 1
jlong dobject = (jlong) env->GetObjectField(object ,fid);
cout <<dobject; //displays 139757766370904
// case 2 - value 2
long dobject2 = (long) env->GetObjectField(object, fid);
cout <<dobject2; // displays 140031771862616
// case 3 - value 3
long long dobject3 = (long long) env->GetObjectField(object ,fid);
cout <<dobject3; //displays 140456034100824
I wonder what would be the best way to get correct value here from Java in C++. I'm using C++ 11. Some one please help me here.
Update
Sorry guys, i think I misspelled a few variables, so it wasn't dobject2,3 in GetObjectfield (correcting that)and infact rewriting the code here to make it more clear.
//Data.java
class Data {
public long sDate;
public Date schedDate;
....
}
// Test.java
// Native function
public native static void sendEvent(Data EventRec);
Data Rec= new DataRec();
Rec.sDate= 1400754723399L;
Rec.schedDate = new Date(1220227200L * 1000);
sendEvent(Rec);
//C++
JNIEXPORT void JNICALL Java_Test_sendEvent (JNIEnv *env, jclass cls, jobject jobj) {
cls = env->FindClass("Data");
if (cls !=NULL) {
jmethodID ctorID = env->GetMethodID(cls, "<init>","()V");
if (ctorID != NULL) {
jfieldID fidLong = env->GetFieldID(cls, "sDate", "J");
long dObj = (long) env->GetLongField(jobj,fidLong);
cout << "C++ .. Event Date (LONG): " << dObj <<endl; // This is correct
jfieldID fidDate = env->GetFieldID(cls, "schedDate", "Ljava/util/Date");
// Here is the problem area, that I tried to mimic above...
jobject dobject= (env->GetObjectField(jobj,fidDate));
long dobj = env->GetLongField(dobject,fidDate);
// OR
// long dObj2 = (long) env->GetLongField(jobj,fidDate);
// .....
cout << "C++ .. Date (DATE)..." << dobj;
}
}
}
Your JNI code is not accessing the Java objects correctly.
In your Java_Test_sendEvent() implementation, the jclass parameter points to the class type that your public native static void sendEvent(Data EventRec); is declared in (which you did not show), and the jobject parameter points to the Data Rec object that your Java code is passing in to sendEvent().
Also, in your call to env->GetFieldID() for the Date field, you are missing a required semicolon at the end of the type signature string.
Try this instead:
JNIEXPORT void JNICALL Java_Test_sendEvent (JNIEnv *env, jclass cls, jobject EventRec)
{
jclass cls_EventRec = env->GetObjectClass(EventRec);
jfieldID fid_sDate = env->GetFieldID(cls_EventRec, "sDate", "J");
if (!fid_sDate) {
// error handling...
return;
}
jfieldID fid_schedDate = env->GetFieldID(cls_EventRec, "schedDate", "Ljava/util/Date;");
if (!fid_schedDate) {
// error handling...
return;
}
jlong sDate = env->GetLongField(EventRec, fid_sDate);
cout << "C++ .. Event Date (LONG): " << sDate << endl;
jobject schedDate = env->GetObjectField(EventRec, fid_schedDate);
if (schedDate) {
jclass cls_schedDate = env->GetObjectClass(schedDate);
jmethodID mid_getTime = env->GetMethodID(cls_schedDate, "getTime", "()J");
jlong timeInMillisecond = env->CallLongMethod(schedDate, mid_getTime);
cout << "C++ .. Date (DATE)..." << timeInMillisecond << endl;
}
}
You're overlooking something very important:
jfieldID fid = env->GetFieldID(cls, "date1", "Ljava/util/Date;");
jobject dobject = env->GetObjectField(dobject, fid); // Note: "Get Object Field"
This returns a java.util.Date object. You need to call getTime on it as well:
jmethodID mid = env->GetMethodID(cls, "getTime", "()J");
jlong timeInMillisecond = env->CallLongMethod(dobject, mid);

How to get a Java string from a char pointer in C++

I am porting the openvr sample to jogl, after we created the binding with jna.
Almost at the end (before rendering the controllers and the tracking base stations), I got stuck trying to translate a char pointer in C to a String in Java.
C++ code here:
//-----------------------------------------------------------------------------
// Purpose: Helper to get a string from a tracked device property and turn it
// into a std::string
//-----------------------------------------------------------------------------
std::string GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL )
{
uint32_t unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError );
if( unRequiredBufferLen == 0 )
return "";
char *pchBuffer = new char[ unRequiredBufferLen ];
unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
std::string sResult = pchBuffer;
delete [] pchBuffer;
return sResult;
}
GetStringTrackedDeviceProperty here:
/** Returns a string property. If the device index is not valid or the property is not a string type this function will
* return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing
* null. Strings will generally fit in buffers of k_unTrackingStringSize characters. */
virtual uint32_t GetStringTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;
Where VR_OUT_STRING() is defined here as:
# define VR_CLANG_ATTR(ATTR)
#define VR_OUT_STRING() VR_CLANG_ATTR( "out_string: ;" )
I have already done something similar where I had to call a function that expect the pointer to an array of TrackedDevicePose_t structures:
private TrackedDevicePose_t.ByReference trackedDevicePosesReference = new TrackedDevicePose_t.ByReference();
public TrackedDevicePose_t[] trackedDevicePose
= (TrackedDevicePose_t[]) trackedDevicePosesReference.toArray(VR.k_unMaxTrackedDeviceCount);
I created first the reference and then from it the actual array.
But here I can't have a class extending the char array..
private String getTrackedDeviceString(IVRSystem hmd, int device, int prop, IntBuffer propError) {
int requiredBufferLen = hmd.GetStringTrackedDeviceProperty.apply(device, prop, Pointer.NULL, 0, propError);
if(requiredBufferLen == 0) {
return "";
}
CharArray.ByReference charArrayReference = new CharArray.ByReference();
char[] cs = charArrayReference.toArray(requiredBufferLen);
return null;
}
Where apply (here) is:
public interface GetStringTrackedDeviceProperty_callback extends Callback {
int apply(int unDeviceIndex, int prop, Pointer pchValue, int unBufferSize, IntBuffer pError);
};
CharArray class, crap attempt here
Any ideas?
I've done some porting of C and C++ code to Java, and while it's probably horribly hacky, the best I've come up with to solve cases where a pointer to an int primitive or a char*/String is needed for a function call, is to create a small wrapper class with a single property, pass that object into the function, change the property as needed, and retrieve the new value after the function call. So something like:
public class StringPointer {
public String value = "";
}
StringPointer pchBuffer = new StringPointer();
unRequiredBufferLen = pHmd.GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
String sResult = pchBuffer.value;
and inside GetStringTrackedDeviceProperty()
...
pchValue.value = "some string";
...
In this case, you can use a String, since that's what your code is doing with the char* after the function call, but if it actually really needs to be a char[], you can just create char[] pchBuffer = new char[unRequiredBufferLen]; and pass that into the function. It will be just like you were using a char* in C++, and any changes you make inside the array will be visible after the function ends, and you can even do String sResult = new String(pchBuffer);.

Having problems converting jByteArray to .NET byte[ ]

I am having problems working with JNI and have been stuck on this issue for quite some time. I have posted about this before, but never received an answer and have done lots of research between now and then.
My JNI signature:
JNIEXPORT void JNICALL Java_MyApplet_invokeManager(JNIEnv *jniEnvPtr, jobject javaObj, jbyteArray encodedData)
Some of my code:
boolean isCopy;
jbyte* bytes = jniEnvPtr->GetByteArrayElements(encodedData, &isCopy);
jniEnvPtr->ReleaseByteArrayElements(encodedData, bytes, JNI_ABORT);
myManager->ShowQueue(encodedData);
The error message:
error C2664: 'MyModule::JniToManaged::ShowFormQueue' : cannot convert parameter 2 from 'jbyte *' to 'cli::array<Type,dimension> ^'
I have found no way to convert this to the C# byte[] that I need to pass. I have heard about casting the jbyte* object but can't figure out how to get it into the correct format.
I figured out how to convert a jbytearray to cli::array. Here is the code:
jbytearray jArray; //my array
jint len = jniEnvPtr->GetArrayLength(jArray); //get length
boolean isCopy;
jbyte* b = jniEnvPtr->GetByteArrayElements(jArray, &isCopy); //get pointer
array<byte, 1> ^myArray = gcnew array<byte, 1>(len); //create the cli::array
//loop through jbytearray and copy elements into our cli::array
for(int i = 0; i < len; i++)
{
myArray[i] = b[i];
}
jniEnvPtr->ReleaseByteArrayElements(jArray, b, JNI_ABORT); // release
I also had to convert a jstring to System::String^...
jboolean blnIsCopy;
jstring jstrOutput;
//jstring to char*
const char* strA = (jniEnvPtr)->GetStringUTFChars(theJString, &blnIsCopy);
//char* to std::string
std::string standardStr(strA);
//std:string to System::String^
System::String^ str2 = gcnew System::String(standardStr.c_str())
MessageBox(NULL, standardStr.c_str(), "Report Name!", MB_OK);
jniEnvPtr->ReleaseStringUTFChars(theJString, strA);
If there are no answers, it means (a) nobody knows, (b) nobody cares (Posting .net stuff under C++ is a sure fire way to get there. Posting an error message that should be easy to fix is another.), or (c) it might be a case of "What the heck is OP trying to do?! This makes no sense whatsoever. Nope, no head nor tail. Moving on..."