在jobjectarray中使用params调用NewObject方法jni

时间:2015-06-17 19:19:04

标签: java c++ java-native-interface

我正在使用c ++在JNI中工作,我创建了一个方法,其中一系列参数作为jobjectarray传递给我的本机方法。我想使用这些参数在JNI中调用构造函数。但是,NewObject方法不接受使用省略号的jobject数组。我该如何完成这项任务?我不知道构造函数在调用方法之前将采用多少参数,并且签名字符串也是从java传递的。我调用的构造函数不会将数组作为参数,而是可以将同一类的不同版本传递给c ++函数,每个函数都包含不同的方法签名。我需要我的c ++方法通常能够使用传递的参数创建任何对象。我使用visual studio作为我的IDE。我知道我可能需要一组jvalues但我不明白如何从jobjectarray中获取它。

2 个答案:

答案 0 :(得分:2)

编辑:

抱歉,我误解了你的问题。您可以使用JNI API为创建对象提供的另外两种方式(来自docs)来实现此目的:

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

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

<强> NewObjectA

  

程序员将所有要传递给构造函数的参数放在紧跟在methodID参数之后的jvalues的args数组中。 NewObjectA()接受此数组中的参数,然后将它们传递给程序员希望调用的Java方法。

<强> NewObjectV的

  

程序员将所有要传递给构造函数的参数放在紧跟在methodID参数之后的类型为va_list的args参数中。 NewObjectV()接受这些参数,然后将它们传递给程序员希望调用的Java方法。

所以,我制作了一个示例程序,展示了如何使用它。

<强> 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();
    }
}

警告:它仍然不是100%函数,因为我无法弄清楚如何根据构造函数签名检索构造函数参数大小。

答案 1 :(得分:1)

这有点棘手,因为你已经通过jobjectArray。这意味着原始类型已经装箱(例如int是你的数组中的java.lang.Integer个实例),你必须在将它们传递给构造函数之前将它们取消装箱。

您将要做的是解析方法签名字符串(它不像您想象的那么糟糕),遍历数组中的每个jobject并将其转换为相应的类型(使用必要时进行拆箱转换。

可悲的是,JNI中没有内置的方式来执行拆箱,因此你必须通过调用盒装值的适当方法手动完成(例如Integer.intValue来获取{{1 }}为s)。

基本理念:

int

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将使用JNI在给定对象上调用object_to_byte