Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get values from jobject in C using JNI?

How can I get the values of a jobject in C?
I use JNI and call a java function in C. The parameter is a jobject and it should look like that: {"John", "Ganso", 5}.
Now I want to get the values from that object but I dont know how. Do you have any suggestions how to solve that?
My struct in C looks like my class in java.

My code looks like that:

JNIEXPORT void JNICALL
Java_model_JNIResultSet_printToFile(JNIEnv *env, jobject obj,
    jobject o) {

// How can I get values of jobject o?
}
like image 432
mafioso Avatar asked Oct 12 '16 17:10

mafioso


People also ask

What is JNI Jobject?

jobject thiz means the this in java class. Sometimes if you create a static native method like this. void Java_MyClass_method1 (JNIEnv *, jclass); jclass means the class itself. Follow this answer to receive notifications.

What is Jclass in JNI?

The jclass instance is your object on which a method will be invoked; you'll need to look up the getName method ID on the Class class, then invoke it on the jclass instance using CallObjectMethod to obtain a jstring result. So in short yes, you just call the getName function and look at the jstring result.

What is JNI reference?

A JNI global reference is a reference from "native" code to a Java object managed by the Java garbage collector. Its purpose is to prevent collection of an object that is still in use by native code, but doesn't appear to have any live references in the Java code. A JFrame is a java. awt.

What is JNIEnv * env?

JNIEnv – a structure containing methods that we can use our native code to access Java elements. JavaVM – a structure that lets us manipulate a running JVM (or even start a new one) adding threads to it, destroying it, etc…

Why do we need JNI in Java?

1. Introduction At times, it is necessary to use native (non-Java) codes (e.g., C/C++) to overcome the memory management and performance constraints in Java. Java supports native codes via the Java Native Interface (JNI). JNI is difficult, as it involves two languages and runtimes.

What is jobject and JNIEnv?

JNIEnv*: reference to JNI environment, which lets you access all the JNI functions. jobject: reference to " this " Java object. We are not using these arguments in this hello-world example, but will be using them later.

How do you get the return value from a JNI function?

As the return value from calling a JNI function A valid JNI global reference must be obtained from another valid JNI reference (global or local) by calling NewGlobalRef or NewWeakGlobalRef. The null value reference is always valid, and can be used in place of any JNI reference (global or local).

How do I get the field ID of a JNI object?

Whether you're writing native methods or embedding a JVM in your C program, you really ought to read the JNI documentation. It contains quite a bit of information you will need to know, including details of the JNI functions for accessing the fields of a Java object. Obtain its field ID via GetFieldID ().


3 Answers

/* PassObject.java */
package recipeNo020;

public class PassObject {

    /* This is the native method we want to call */
    public static native void displayObject(Object obj);

    /* Inside static block we will load shared library */
    static {
            System.loadLibrary("PassObject");
    }

    public static void main(String[] args) {
    /* This message will help you determine whether
        LD_LIBRARY_PATH is correctly set
    */
    System.out.println("library: "
        + System.getProperty("java.library.path"));

    /* Create object to pass */
    CustomClass cc = new CustomClass();
    cc.iVal = 1;
    cc.dVal = 1.1;
    cc.cVal = 'a';
    cc.bVal = true;
    cc.sVal = "Hello from the CustomClass";
    cc.oVal = new OtherClass();
    cc.oVal.sVal = "Hello from the OtherClass";

    /* Call to shared library */
        PassObject.displayObject(cc);
  }
}

/* CustomClass.java */
package recipeNo020;

public class CustomClass {
    public int iVal;
    public double dVal;
    public char cVal;
    public boolean bVal;
    public OtherClass oVal;
    public String sVal;
}

/* OtherClass.java */
package recipeNo020;

public class OtherClass {
    public String sVal;
}

/* recipeNo020_PassObject.c */

#include <stdio.h>
#include "jni.h"
#include "recipeNo020_PassObject.h"

JNIEXPORT void JNICALL Java_recipeNo020_PassObject_displayObject
  (JNIEnv *env, jclass obj, jobject objarg) {

    /* Get objarg's class - objarg is the one we pass from
       Java */
    jclass cls = (*env)->GetObjectClass(env, objarg);

    /* For accessing primitive types from class use
           following field descriptors

           +---+---------+
           | Z | boolean |
           | B | byte    |
           | C | char    |
           | S | short   |
           | I | int     |
           | J | long    |
           | F | float   |
           | D | double  |
           +-------------+
    */

    /* Get int field

       Take a look here, we are passing char* with
           field descriptor - e.g. "I" => int
        */
    jfieldID fidInt = (*env)->GetFieldID(env, cls, "iVal", "I");
    jint iVal = (*env)->GetIntField(env, objarg, fidInt);
    printf("iVal: %d\n", iVal);

    /* Get double field */
    jfieldID fidDouble = (*env)->GetFieldID(env, cls, "dVal", "D");
    jdouble dVal = (*env)->GetIntField(env, objarg, fidDouble);
    printf("dVal: %f\n", dVal);

    /* Get boolean field */
    jfieldID fidBoolean = (*env)->GetFieldID(env, cls, "bVal", "Z");
    jboolean bVal = (*env)->GetIntField(env, objarg, fidBoolean);
    printf("bVal: %d\n", bVal);

    /* Get character field */
    jfieldID fidChar = (*env)->GetFieldID(env, cls, "cVal", "C");
    jboolean cVal = (*env)->GetIntField(env, objarg, fidChar);
    printf("cVal: %c\n", cVal);

    /* Get String field */
    jfieldID fidString = (*env)->GetFieldID(env, cls, "sVal", "Ljava/lang/String;");
    jobject sVal = (*env)->GetObjectField(env, objarg, fidString);

    // we have to get string bytes into C string
    const char *c_str;
    c_str = (*env)->GetStringUTFChars(env, sVal, NULL);
    if(c_str == NULL) {
            return;
    }

    printf("sVal: %s\n", c_str);

    // after using it, remember to release the memory
    (*env)->ReleaseStringUTFChars(env, sVal, c_str);

    /* Get OtherClass */
    jfieldID fidOtherClass = (*env)->GetFieldID(env, cls, "oVal", "LrecipeNo020/OtherClass;");
    jobject oVal = (*env)->GetObjectField(env, objarg, fidOtherClass);

    jclass clsOtherClass = (*env)->GetObjectClass(env, oVal);

    /* Once we have OtherClass class and OtherClass object
       we can access OtherClass'es components
    */

    /* Get String field from OtherClass */
    jfieldID fidStringOtherClass = (*env)->GetFieldID(env, clsOtherClass, "sVal", "Ljava/lang/String;");
    jobject sValOtherClass = (*env)->GetObjectField(env, oVal, fidStringOtherClass);

    // we have to get string bytes into C string
    const char *c_str_oc;
    c_str_oc = (*env)->GetStringUTFChars(env, sValOtherClass, NULL);
    if(c_str_oc == NULL) {
        return;
    }

    printf("OtherClass.sVal: %s\n", c_str_oc);

    // after using it, remember to release the memory
    (*env)->ReleaseStringUTFChars(env, sValOtherClass, c_str_oc);
}

/* Make file */

include ../Makefile.common

all: compilejava compilec

compilec:
    cc -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(ARCH) c/recipeNo020_PassObject.c -o lib/libPassObject.$(EXT)


compilejava:
    $(JAVA_HOME)/bin/javac -cp target -d target java/recipeNo020/OtherClass.java
    $(JAVA_HOME)/bin/javac -cp target -d target java/recipeNo020/CustomClass.java
    $(JAVA_HOME)/bin/javac -cp target -d target java/recipeNo020/PassObject.java
    $(JAVA_HOME)/bin/javah -jni -d c -cp target recipeNo020.PassObject

test:
    $(JAVA_HOME)/bin/java -Djava.library.path=$(LD_LIBRARY_PATH):./lib -cp target recipeNo020.PassObject

.PHONY: clean
clean:
    -rm -rfv target/*
    -rm c/recipeNo020_PassObject.h
    -rm -rfv lib/*

/* directory structure */
.
├── Makefile
├── c
│   └── recipeNo020_PassObject.c
├── java
│   └── recipeNo020
│       ├── CustomClass.java
│       ├── OtherClass.java
│       └── PassObject.java
├── lib
└── target

/* execution */
make
make test

You can find it easier to checkout the project and compile it:

https://github.com/mkowsiak/jnicookbook

like image 111
Oo.oO Avatar answered Oct 17 '22 02:10

Oo.oO


Whether you're writing native methods or embedding a JVM in your C program, you really ought to read the JNI documentation. It contains quite a bit of information you will need to know, including details of the JNI functions for accessing the fields of a Java object.

In brief, to get the value of a field, you

  1. Obtain its field ID via GetFieldID(). This will require that you also get (or already have) a jclass object representing the class to which the field belongs; you might obtain that via GetObjectClass() or FindClass().

  2. Obtain the field's value with the one of the GetXXXField() methods for which XXX is appropriate to the field's declared type. For Java Strings, that would be GetObjectField(); for Java ints it would be GetIntField().

If you want to look at the details of the strings, you will need to also use some of the String manipulation functions, such as GetStringUTFChars() and GetStringUTFLength(). And do not overlook the important distinction between those functions, which operate in terms of modified UTF-8, and the analogous functions that operate in terms of "Unicode characters" (which really means UTF-16).

like image 20
John Bollinger Avatar answered Oct 17 '22 01:10

John Bollinger


I agree with John Bollinger. Here is an example based on another [question]:JNI. How to get jstring from jobject and convert it to char*

In your case if the class in java is Person and the fields are firstName, lastName, and age then you might try the following code:

    // How can I get values of jobject o?
    jclass personClass = (*env)->GetObjectClass(env, o);
    jfieldID firstNameId = (*env)->GetFieldID(env,personClass,"firstName","S");
    jstring firstNameString = (jstring)(*env)->GetObjectField(env, o, firstNameId);
    jfieldID lastNameId = (*env)->GetFieldID(env,personClass,"lastName","S");
    jstring lastNameString = (jstring)(*env)->GetObjectField(env, o, lastNameId);
    jfieldID ageId = (*env)->GetFieldID(env,personClass,"age","I");
    jint age = (*env)->GetIntField(env,o,ageId);

Now you can use that data to fill your struct.

like image 37
user1759789 Avatar answered Oct 17 '22 01:10

user1759789