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