2015-06-17 3 views
3

Я работаю в JNI с C++, и я создал метод, в котором ряд параметров передается моему нативного методу как jobjectarray. Я хотел бы вызвать конструктор в JNI, используя эти параметры. Однако метод NewObject не принимает массив заданий вместо использования многоточия. Как бы я выполнил эту задачу? Я не знаю, сколько параметров конструктор будет принимать до вызова метода, и строка подписи передается также из java. Конструктор, который я вызываю, не принимает массив в качестве аргумента, вместо этого различные функции одного и того же класса могут быть переданы функции C++, каждая из которых содержит другую подпись метода. Мне нужен мой метод C++, чтобы он мог создать любой объект с его переданными аргументами. Я использую визуальную студию в качестве моей IDE. Я понимаю, что мне может понадобиться массив jvalues, но я не понимаю, как получить это из jobjectarray.Вызов метода NewObject jni с параметрами в jobjectarray

+0

Вы считали использование нескольких аргументов, а не только одного массива? Это упростит ситуацию. – tbodt

+0

"call a constructor": вы имеете в виду конструктор java? И как вы находите подходящего конструктора, глядя на соответствующую подпись? – Christophe

+0

Я не знаю, сколько аргументов будет принимать конструктор до вызова метода. – popgalop

ответ

1

EDIT:

К сожалению, я не понял ваш вопрос. Вы можете добиться этого с помощью двух других способов, которые 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

Программисты место все аргументы, которые должны быть переданы в конструктор в массиве аргументов jvalues, который сразу же следует за аргументом methodID. NewObjectA() принимает аргументы в этом массиве и, в свою очередь, передает их методу Java, который программист хочет вызвать.

NewObjectV

Программисты разместить все аргументы, которые должны быть переданы в конструктор в ARGS аргумент типа va_list, который непосредственно следует аргумент methodID. 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% funciontal, потому что я не мог понять, как получить размер аргумента конструктора на основе конструктора подписи.

+0

Это не совсем то, что я пытаюсь сделать. Конструктор, который я вызываю, не принимает массив в качестве аргумента. Проблема в том, что разные версии одного и того же класса могут быть переданы функции C++, каждая из которых содержит другую подпись метода. Мне нужен мой метод C++, чтобы он мог создать любой объект с его переданными аргументами. – popgalop

1

Это немного сложно, потому что вы прошли мимо jobjectArray. Это означает, что примитивные типы были помещены в бокс (например, int s - это java.lang.Integer экземпляров в вашем массиве), и вам необходимо их удалить, прежде чем передавать их на конструктор.

Что вам нужно сделать, так это разобрать строку подписи метода (это не так плохо, как вы могли бы подумать), пройти через каждый jobject в свой массив и преобразовать его в соответствующий тип (с использованием преобразования unboxing если необходимо).

К сожалению, нет никакого встроенного способа в JNI для выполнения распаковки, и, таким образом, вы будете иметь, чтобы сделать это вручную, вызывая соответствующие методы боксовых значений (например, Integer.intValue получить 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 и другие функции преобразования будут определены как функции, которые распаковывать соответствующий тип (например, object_to_byte будет использовать JNI для вызова java.lang.Byte.byteValue на данном объекте).

Смежные вопросы