Passing a list/arraylist of reference type objects contained in a class to JNI/C++ - java-native-interface

I wrote a piece of code that manipulates reference type objects contained inside a class, now am having trouble while converting this instance to a list of objects.
Here is the code snippet;
classes.java
=================
public class Leg {
public int id;
public String name;
// a few other primitive data types
....
}
public class Line {
public int id;
....
//public Leg mLeg; this worked well
public List<Leg> mLegList;
}
Driver.java
============
public native void setLine(Line jobj);
Line line = new Line();
line.id =200;
line.mLegList = new ArrayList<Leg>(1); // using only one for brevity
Leg obj = new Leg(200, "test", ...);
line.mLegList.add(obj);
Native CPP function
==================
JNIEXPORT void JNICALL Java_Driver_setLine (JNIEnv * env, jobject jobj1, jobject jobj)
{
jclass cls = env->GetObjectClass(jobj);
if (cls == NULL)
return;
jmethodID ctorID = env->GetMethodID(cls, "<init>","()V");
if (!ctorID)
return ;
// I did for one instance like this
/*************************************************************
* jfieldID fid_legID = env->GetFieldID(cls, "mLeg", "LLeg;");
* if (!fid_legID)
* return;
* jobject legObject = env->GetObjectField(jobj, fid_legID);
* jclass clsObject = env->GetObjectClass(legObject);
* if (clsObject) {
* // Get all fields of Leg inside Line ....
*************************************************************/
// Now as i changed it to list/arraylist of Legs, it isn't Working
jfieldID fid_legID = env->GetFieldID(cls, "mLegList", "[LLeg;");
if (!fid_legID)
env->ExceptionDescribe();
// Exception in thread "main" java.lang.NoSuchFieldError: mLegList
}
My questions are:
how to make it working with a list/ArrayList here. I need to
retrieve a set of fields contained in this list.
Why isn't the
native function signature included jobjectarray (or something similar
for jobj1?) Is that causing issues? Thanks for reading the post.
Update
I was able to construct the object successfully as using Arraylist/List require to use
fieldID fid_legID = env->GetFieldID(cls, "mLegList", "Ljava/util/List;");
instead. but now I'm wondering how to iterate through this list and read through all the fields.

Let's say your source code tree looks like this
|-- c
| `-- Driver.c
|-- lib
|-- mypackage
| |-- Driver.java
| |-- Leg.java
| `-- Line.java
`-- target
and then, you have following files:
mypackage/Driver.java
package mypackage;
import java.util.ArrayList;
public class Driver {
static {
System.loadLibrary("Driver");
}
public void list(ArrayList<Leg> list) {
}
public native void setLine(Line line);
public static void main(String [] arg) {
Driver d = new Driver();
Line line = new Line();
line.id = 200;
line.mLegList = new ArrayList<Leg>(1);
Leg obj_1 = new Leg(200, "test_1");
Leg obj_2 = new Leg(300, "test_2");
Leg obj_3 = new Leg(400, "test_2");
line.mLegList.add(obj_1);
line.mLegList.add(obj_2);
line.mLegList.add(obj_3);
d.setLine( line );
}
}
mypackage/Leg.java
package mypackage;
class Leg {
public int id;
public String name;
public Leg(int id, String name) {
this.id = id;
this.name = name;
}
}
mypackage/Line.java
package mypackage;
import java.util.List;
public class Line {
public int id;
public List<Leg> mLegList;
}
You can access elements of the List following way
c/Driver.c
#include "mypackage_Driver.h"
JNIEXPORT void JNICALL Java_mypackage_Driver_setLine(JNIEnv * env, jobject jobj, jobject line)
{
jclass cls_Line = (*env)->GetObjectClass (env, line);
jfieldID fid_mLegList = (*env)->GetFieldID (env, cls_Line, "mLegList", "Ljava/util/List;");
jobject list = (*env)->GetObjectField (env, line, fid_mLegList);
jclass cls_list = (*env)->GetObjectClass (env, list);
int listSize = (*env)->GetArrayLength (env, list);
for (int i = 0; i < listSize; i++) {
jmethodID midGet = (*env)->GetMethodID (env, cls_list, "get",
"(I)Ljava/lang/Object;");
jstring obj = (*env)->CallObjectMethod (env, list, midGet, i);
jclass cls_Leg = (*env)->GetObjectClass(env, obj);
jfieldID fid_name = (*env)->GetFieldID( env, cls_Leg, "name",
"Ljava/lang/String;");
jobject name = (*env)->GetObjectField (env, obj, fid_name);
const char *c_string = (*env)->GetStringUTFChars (env, name, 0);
printf ("[value] = %s\n", c_string);
(*env)->ReleaseStringUTFChars (env, obj, c_string);
}
}
If you want to compile all the stuff, you can use following script
#!/bin/bash
mkdir -p target
mkdir -p lib
ARCH=`uname -s | tr '[:upper:]' '[:lower:]'`
EXT=
if [[ "${ARCH}" == "darwin" ]]; then
EXT=dylib
else
EXT=so
fi
echo $ARCH
javac -h c -d target mypackage/*.java
cc -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/${ARCH} c/Driver.c -o lib/libDriver.${EXT}
${JAVA_HOME}/bin/java -Djava.library.path=${LD_LIBRARY_PATH}:./lib -cp target mypackage.Driver
Once you run it, you will get what you are looking for :)
> ./compile.sh
darwin
[value] = test_1
[value] = test_2
[value] = test_2
Have fun with JNI :)
Update
accessing array elements using get method: recipeNo067
accessing array elements using Iterator: recipeNo068
passing ArrayList as Object []: recipeNo069
Usage
> git clone https://github.com/mkowsiak/jnicookbook.git
> export JAVA_HOME=your_JDK_installation
> cd jnicookbook/recipes/recipeNo067
> make
> make test

Iterating across a List in JNI is the same as in Java, it is just more typing.
jobject list = env->GetObjectField(line, fid_legID);
// Iterator iterator = list.iterator();
jclass cls_List = env->FindClass("java/util/List");
jmethodID mid_List_iterator = env->GetMethodID(cls_List, "iterator", "()Ljava/util/Iterator;");
jobject iterator = env->CallObjectMethod(list, mid_List_iterator);
jclass cls_Iterator = env->FindClass("java/util/Iterator");
jmethodID mid_Iterator_hasNext = env->GetMethodID(cls_Iterator, "hasNext", "()Z");
jmethodID mid_Iterator_next = env->GetMethodID(cls_Iterator, "next", "()Ljava/lang/Object");
while (true) {
// if !iterator.hasNext() break
jboolean hasNext = env->CallBooleanMethod(iterator, mid_Iterator_hasNext);
if (!hasNext)
break;
jobject v = env->CallObjectMethod(iterator, mid_Iterator_next);
// Do something with `v`
// Avoid overflowing the local reference table if legList gets large
env->DeleteLocalRef(v);
}
Note that this example still needs error checking.

Related

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

How to Pass and return Array of properties in jni (java to c++) as well as (c++ to 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.

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);

Passing String argument from c to java using jni

I am trying to call java method from C, but getting below error
Status after JNI_CreateJavaVM=<0>
restCall method found:: Avinash Kumar
Execution error : file ''
error code: 114, pc=0, call=1, seg=0
114 Attempt to access item beyond bounds of memory (Signal 11)
Target is to send String as an argument from C to Java function.
Below is the code :
#include <jni.h>
#define ENV (*env)
enum eConst
{
MAX_Options = 21 ,
MAX_LREF = 120 ,
PARSING_ERROR = -10 ,
ERROR = -999 ,
EXCEPTION_ERROR = -100 ,
OK = 1 ,
YES = 'Y' ,
NO = 'N'
};
int main()
{
jint jRet;
jstring jszRes;
static JavaVM *jvm = NULL;
static JNIEnv *env = NULL ;
JavaVMInitArgs vm_args;
JavaVMOption options[MAX_Options-1];
int optionCount=0;
options[optionCount++].optionString = "-verbose:jni,class,gc";
char *path="user/work/avikumar/";
char *class_path="user/work/avikumar/";
char path_option[2000]={'\n'};
sprintf(path_option,"-Djava.class.path=%s/MyClass.class:%s/jersey-core-1.19.jar:%s/jersey-client-1.19.jar:%s/javax.ws.rs-api-2.0-m02.jar:.",class_path,path,path,path);
options[optionCount++].optionString = path_option;
options[optionCount++].optionString = "-Xms128m";
options[optionCount++].optionString = "-Xmx512m";
options[optionCount++].optionString = "-Xss8m";
vm_args.options = options;
vm_args.nOptions = optionCount;
vm_args.version = JNI_VERSION_1_6;
vm_args.ignoreUnrecognized = JNI_FALSE;
jRet = JNI_CreateJavaVM(&jvm,(void**)&env,(void*)&vm_args);
printf("Status after JNI_CreateJavaVM=<%d>\n",jRet);
if (jRet < 0)
{
return(-4);
}
if( ENV->EnsureLocalCapacity(env, MAX_LREF) < 0)
{
printf( "\n out of memory. Program terminated\n");
(*jvm)->DestroyJavaVM(jvm);
return(-5);
}
jclass jlocClass;
jlocClass= ENV->FindClass(env, "MyClass");
if (jlocClass == NULL || (*env)->ExceptionOccurred(env))
{
printf("Can not load the main class\n");
return (-6);
}
jmethodID restCall_method;
restCall_method = ENV->GetMethodID(env, jlocClass,"restCall","(Ljava/lang/String;)V");
if( restCall_method == NULL)
{
printf("restCall method not found\n");
return (-7);
}
char *inp = "Avinash Kumar";
printf("restCall method found:: %s\n",inp);
//jstring jstr = (*env)->NewStringUTF(env,inp);
jstring jstr1=(*env)->NewStringUTF(env, "Avinash Kumar");
ENV->CallVoidMethod(env, jlocClass, restCall_method,jstr1);
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
MyClass.java
class MyClass
{
public static void main(String args[])
{
System.out.println("Hello Avinash\n");
}
public void restCall(String inp)
{
System.out.println(inp+ " :: Hello Avinash from restCall\n");
}
}
You can't mix static/non-static context inside JNI. You have two choices.
Static method
restCall_method = ENV->GetStaticMethodID(
env, jlocClass, "restCall", "(Ljava/lang/String;)V");
...
...
...
ENV->CallStaticVoidMethod(env, jlocClass, restCall_method,jstr1);
and, inside Java
public static void restCall(String inp)
or non-static method
jmethodID init = ENV->GetMethodID(env,jlocClass, "<init>", "()V");
jobject obj_MyClass = ENV->NewObject(env,jlocClass, init);
jmethodID restCall_method;
restCall_method = ENV->GetMethodID(env, jlocClass,"restCall","(Ljava/lang/String;)V");
...
...
...
ENV->CallVoidMethod(env, obj_MyClass, restCall_method,jstr1);
and, inside Java
public void restCall(String inp)
In first case, you have to use Static family methods to get id and call target method, in second case, you have to create instance of class and use methods for object based context.

Jni C++ findclass function return null

I found a problem with jni about C calling Java code.
Environment WIN10 JDK1.8
Currently I need C++ code to call Java code. At first I wrote a demothat was successful. Code show as below:
public class Sample2 {
public String name;
public static String sayHello(String name) {
return "Hello, " + name + "!";
}
public String sayHello() {
return "Hello, " + name + "!";
}
}
Some of the C++ code is as follows:
int main(){
printf("hello world");
JavaVMOption options[3];
JNIEnv* env;
JavaVM* jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
jfieldID fid;
jobject obj;
char opt1[] = "-Djava.compiler=NONE";
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
options[0].optionString = opt1; options[0].extraInfo = NULL;
options[1].optionString = opt2; options[1].extraInfo = NULL;
options[2].optionString = opt3; options[2].extraInfo = NULL;
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = 0;
// 启动虚拟机
status = JNI_CreateJavaVM(&jvm, (void**)& env, &vm_args);
if (status != JNI_ERR){
// 先获得class对象
cls = env->FindClass("Sample2");
}
}
I used Eclipse to compile the Java code into a .class file, copy the .class file into my C++ project, the above DEMO C++ call Java function is successful, and the findclass function returns to normal.
Because I have to introduce a third-party JAR package org.eclipse.paho.client.mqttv3-1.2.0.jar in my own Java, based on the above example, I modified the Java code in DEMO, but when I want to reference the JAR package function, And then run successfully in Eclipse, when I copy the .class file to the C++ project. JNI_CreateJavaVM in the C++ code is returned successfully, but FINDCLASS always returns null, I don't know why. I have not changed the other parts code.
Some Java code:
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Sample2 {
public String name;
static MqttAsyncClient mqttClient = null;
static String username = "xxx";
static String password = "xxx";
static String broker = "xxx";
public static void main(String[] args) throws InterruptedException {
System.out.print("hello");
}
public static void start() {
String clientId = "mqttserver" + String.valueOf(System.currentTimeMillis());
try {
mqttClient = new MqttAsyncClient(broker, clientId, new MemoryPersistence());
} catch (Exception me) {
me.printStackTrace();
}
}
When in start function is added
mqttClient = new MqttAsyncClient(broker, clientId, new MemoryPersistence()); After the code, there will be problems
Take a look here
char opt1[] = "-Djava.compiler=NONE";
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
options[0].optionString = opt1; options[0].extraInfo = NULL;
options[1].optionString = opt2; options[1].extraInfo = NULL;
options[2].optionString = opt3; options[2].extraInfo = NULL;
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
You are passing three options (array of options with three options defined) but then, you say something like this
vm_args.nOptions = 1;
which means you are passing just one option. It means your options
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
are not even read. You have to change your code to
vm_args.nOptions = 3;
Also, make sure to put on java.class.path all the JARs, folders, where classes required by your code are.