I have native method that returns Class<?>[] value. I trying to put jclass as jobject element to object array. But when i calling my native method from java, happens nothing (seriously, just nothing, method wont finish executing). There my code:
... other code where i makes some magic with classes
jobjectArray out = env->NewObjectArray(count /* count is classes count that i want to return */, caller /* method caller */, NULL);
for (int i = 0; i < count; ++i) {
jclass clazz = classes[i];
env->SetObjectArrayElement(out, i, clazz);
}
return out;
Related
i’m making a dll in c++ that reads fields via jni. after a while whenever the game garbage collects it stutters.
the memory view in visual studio doesn’t show any change in memory usage so i doubt it’s a memory leak.
this is where i assume it’s happening but i cannot see why / how it happens.
how can i figure out what’s causing the garbage collector to take so long?
jobject get_class_loader() {
jclass thread_clazz = env->FindClass("java/lang/Thread");
jmethodID curthread_mid = env->GetStaticMethodID(thread_clazz, "currentThread", "()Ljava/lang/Thread;");
jobject thread = env->CallStaticObjectMethod(thread_clazz, curthread_mid);
jmethodID threadgroup_mid = env->GetMethodID(thread_clazz, "getThreadGroup", "()Ljava/lang/ThreadGroup;");
jclass threadgroup_clazz = env->FindClass("java/lang/ThreadGroup");
jobject threadgroup_obj = env->CallObjectMethod(thread, threadgroup_mid);
jmethodID groupactivecount_mid = env->GetMethodID(threadgroup_clazz, "activeCount", "()I");
jfieldID count_fid = env->GetFieldID(threadgroup_clazz, "nthreads", "I");
jint activeCount = env->GetIntField(threadgroup_obj, count_fid);
jobjectArray arrayD = env->NewObjectArray(activeCount, thread_clazz, NULL);
jmethodID enumerate_mid = env->GetMethodID(threadgroup_clazz, "enumerate", "([Ljava/lang/Thread;)I");
env->CallIntMethod(threadgroup_obj, enumerate_mid, arrayD);
jobject array_elements = env->GetObjectArrayElement(arrayD, 0);
jmethodID threadclassloader = env->GetMethodID(thread_clazz, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
if (threadclassloader != NULL) {
jobject classloader_obj = env->CallObjectMethod(array_elements, threadclassloader);
env->DeleteLocalRef(array_elements);
env->DeleteLocalRef(thread_clazz);
env->DeleteLocalRef(thread);
env->DeleteLocalRef(threadgroup_clazz);
env->DeleteLocalRef(threadgroup_obj);
env->DeleteLocalRef(arrayD);
return classloader_obj;
}
else {
env->DeleteLocalRef(array_elements);
env->DeleteLocalRef(thread_clazz);
env->DeleteLocalRef(thread);
env->DeleteLocalRef(threadgroup_clazz);
env->DeleteLocalRef(threadgroup_obj);
env->DeleteLocalRef(arrayD);
return 0;
}
}
jclass get_object(const char* class_name) {
jclass clazz = env->FindClass("java/lang/Class");
jmethodID class_for_name_method = env->GetStaticMethodID(clazz, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
if (class_for_name_method == NULL) {
env->FatalError("Class.forName(String,boolean,java.lang.ClassLoader)java.lang.Class method not found\n");
}
jstring class_to_load = env->NewStringUTF(class_name);
auto class_loader = get_class_loader();
jclass jar_class = (jclass)env->CallStaticObjectMethod(clazz, class_for_name_method, class_to_load, true, class_loader);
env->ReleaseStringUTFChars(class_to_load, NULL);
env->DeleteLocalRef(class_to_load);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(class_loader);
return jar_class;
}
instance = std::make_unique<c_minecraft>();
auto minecraft_inst = instance->get_minecraft();
if (!minecraft_inst) {
env->DeleteLocalRef(minecraft_inst);
return;
} // do field suff after here. once done that delete local reference of minecraft_inst
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
How to Pass and return Array of properties in jni (java to c++) as well as (c++ to java)
import java.util.*;
public class Test {
public native static Properties[] getStudentDetails();
public static void main(String[] args) {
System.loadLibrary("Sample");
int a= 10;
Properties[] records = getStudentDetails();
for(Properties record:records){
System.out.print("\ntype:"+record.getProperty("type"));
System.out.print("\ttime:"+record.getProperty("time"));
System.out.print("\tsource:"+record.getProperty("source"));
System.out.print("\teid:"+record.getProperty("eid"));
System.out.println("");
}
}
}
it give me error
Assuming you have access to a std::vector<SearchRecord*> searchRecordResult somewhere:
extern "C"
JNIEXPORT jobjectArray JNICALL Java_Test_getStudentDetails(JNIEnv *env, jclass cls) {
// Get Properties class, its constructor and the put method
jclass cls_Properties = env->FindClass("java/util/Properties");
jmethodID mid_Properties_ctor = env->GetMethodID(cls_Properties, "<init>", "()V");
jmethodID mid_Properties_put = env->GetMethodID(cls_Properties, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
// Construct the key Strings up front
jstring key_type = env->NewStringUTF("type");
jstring key_time = env->NewStringUTF("time");
jstring key_source = env->NewStringUTF("source");
jstring key_eid = env->NewStringUTF("eid");
jobjectArray ret = env->NewObjectArray(searchRecordResult.size(), cls_Properties, 0);
for (int i = 0; i < searchRecordResult.size(); i++) {
auto result = searchRecordResult[i];
// Allocate and fill a Properties object, making sure to clean up the value Strings.
env->PushLocalFrame(5);
jobject prop = env->NewObject(cls_Properties, mid_Properties_ctor);
env->CallObjectMethod(prop, mid_Properties_put, key_type, env->NewStringUTF(result->type));
env->CallObjectMethod(prop, mid_Properties_put, key_time, env->NewStringUTF(result->time));
env->CallObjectMethod(prop, mid_Properties_put, key_source, env->NewStringUTF(result->source));
env->CallObjectMethod(prop, mid_Properties_put, key_eid, env->NewStringUTF(result->eid));
prop = env->PopLocalFrame(prop);
env->SetObjectArrayElement(ret, i, prop);
}
return ret;
}
The core loop just constructs Properties objects, fills their properties with put and assigns them to the right slot on the ret array.
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);
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.