Calling NewObject jni method with parameters in jobjectarray

I am working in JNI with C ++ and I created a method where a series of parameters is passed to my native method as a jobjectarray. I would like to call a constructor in JNI using these parameters. However, the NewObject method does not accept an array of jobs instead of using an ellipsis. How would I accomplish this task? I don't know how many parameters the constructor will take before calling the method and the signature string is passed from java as well. The constructor I call does not take an array as an argument, instead, different functions of the same class can be passed to C ++ functions, each containing a different method signature. I need my C ++ method so that it can create any object with its passed arguments. I am using visual studio as my IDE. I understand that I might need the jvalues ​​array, but I don't understand,how to get this from jobjectarray.

+3


source to share


2 answers


EDIT:

Sorry, I misunderstood your question. You can achieve this using two other ways the JNI API provides for creating objects (from the docs ):

jobject NewObjectA(JNIEnv *env, jclass clazz,
jmethodID methodID, const jvalue *args);

jobject NewObjectV(JNIEnv *env, jclass clazz,
jmethodID methodID, va_list args);

      

NewObjectA

Programmers put all the arguments to be passed to the constructor in the args array of jvalues, which immediately follows the methodID argument. NewObjectA () takes arguments in this array and in turn passes them to the Java method the programmer wants to call.

NewObjectV

Programmers put all the arguments that must be passed to the constructor in the args argument of type va_list, which immediately follows the methodID argument. NewObjectV () takes these arguments and in turn passes them to the Java method that the programmer wants to call.

So, I made a sample program that shows how to use it.

Foo.java

public class Foo {

    private int bar;
    private String baaz;

    public Foo(int bar) {
        this(bar, "");
    }

    public Foo(int bar, String baaz) {
        this.bar = bar;
        this.baaz = baaz;
    }

    public void method1() {
        this.bar++;

        System.out.println(bar);
        System.out.println(baaz);
    }
}

      



Bar.java

public class Bar {

    public Bar() {
    }

    public static native Foo createFoo(String signature, Object ... params);
}

      

bar.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Bar */

#ifndef _Included_Bar
#define _Included_Bar
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Bar
 * Method:    createFoo
 * Signature: (Ljava/lang/String;[Ljava/lang/Object;)LFoo;
 */
JNIEXPORT jobject JNICALL Java_Bar_createFoo
  (JNIEnv *, jclass, jstring, jobjectArray);

#ifdef __cplusplus
}
#endif
#endif

      

Bar.c

#include "Bar.h"

#include <stdlib.h>

jobject JNICALL Java_Bar_createFoo
  (JNIEnv * env, jclass class, jstring signature, jobjectArray params) {

    // method signature in char *
    const char * signatureChar = (*env)->GetStringUTFChars(env, signature, 0);

    jvalue * args;
    int i, size;

    // retrieve foo class
    jclass fooClass = (*env)->FindClass(env, "LFoo;");

    // retrieve foo construtor
    jmethodID fooConstructor = (*env)->GetMethodID(env, fooClass, "<init>", signatureChar);

    // operate over params
    // ...

    // TODO: find out a way to retrieve size from constructor
    size = 2;

    args = malloc(size * sizeof(jvalue));

    for (i = 0; i < size; i++) {
        args[i].l = (*env)->GetObjectArrayElement(env, params, i);
    }

    return (*env)->NewObjectA(env, fooClass, fooConstructor, args);
}

      

Main.java

public class Main {

    static {
        System.loadLibrary("YOUR_LIBRARY_NAME_HERE");
    }

    public static void main(String[] args) {
        Foo foo = Bar.createFoo("(ILjava/lang/String;)V", -12312141, "foo");

        System.out.println(foo);

        foo.method1();

        foo = Bar.createFoo("(I)V", -12312141, "foo");

        System.out.println(foo);

        foo.method1();

        foo = Bar.createFoo("(I)V", -12312141);

        System.out.println(foo);

        foo.method1();
    }
}

      

Warning: It is still not 100% funciontal because I couldn't figure out how to get the size of the constructor argument based on the constructor signature.

+1


source


It's a little tricky because it handed over to you jobjectArray

. This means that primitive types have been boxed (for example, int

are instances java.lang.Integer

in your array) and you need to remove them before passing them to the constructor.

What you need to do is parse the method signature string (not as bad as you might think), loop through each jobject

in your array, and convert it to the appropriate type (using unboxing if necessary).

Unfortunately, JNI does not have a built-in way to perform unboxing, and therefore you have to do it manually by calling the appropriate nested value methods (for example, Integer.intValue

to get int

s).



Main idea:

jobject createObject(JNIEnv *env, jclass clazz, jmethodID constructor, const char *argstr, jobjectArray *args) {
    int n = env->GetArrayLength(args);
    jvalue *values = new jvalue[n];
    const char *argptr = argstr;
    for(int i=0; i<n; i++) {
        jobject arg = env->GetObjectArrayElement(args, i);
        if(*argptr == 'B') { /* byte */
            values[i].b = object_to_byte(arg);
        }
        /* cases for all of the other primitive types...*/
        else if(*argptr == '[') { /* array */
            while(*argptr == '[') argptr++;
            if(*argptr == 'L')
                while(*argptr != ';') argptr++;
            values[i].l = arg;
        } else if(*argptr == 'L') { /* object */
            while(*argptr != ';') argptr++;
            values[i].l = arg;
        }
        argptr++;
        env->DeleteLocalRef(arg);
    }
    return env->NewObjectA(clazz, methodID, values);
}

      

object_to_byte

and other conversion functions will be defined as functions that remove the corresponding type (for example, object_to_byte

will use JNI to call java.lang.Byte.byteValue

on a given object).

+1


source







All Articles