调用jni jni带有参数的jni

 2023-01-20    313  

问题描述

我正在使用C ++在JNI工作,并且创建了一种方法,其中一系列参数被传递给我的本机方法作为jobightaray.我想使用这些参数在JNI中调用构造函数.但是,NewObject方法不使用省略号而不是接受求解阵列.我将如何完成这项任务?我不知道在调用该方法之前将采用多少个构造函数,并且签名字符串也从Java传递.我正在调用的构造函数不会将数组作为参数,而是将同一类的不同版本传递给C ++函数,每个函数都包含不同的方法签名.我需要我的C ++方法,以便能够使用其传递的参数创建任何对象.我正在使用Visual Studio作为我的IDE.我了解我可能需要一系列的JVALUE,但我不明白如何从Jobightaray获得它.

推荐答案

编辑:

调用jni jni带有参数的jni

对不起,我误解了您的问题.您可以使用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中. NewObjecta()接受此数组中的参数,然后将它们传递给程序员希望调用的Java方法.

newObjectV

程序员将所有要传递给构造函数的参数都在va_list类型的args参数中,该参数立即遵循methotiD参数. 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

其他推荐答案

这有点棘手,因为您已经传递了jobjectArray.这意味着原始类型已经被装箱(例如int是java.lang.Integer在您的数组中的实例),并且在将它们传递到构造函数上之前,您必须将它们拆开.

您将要做的是解析方法签名字符串(它不如您想象的那么糟)如有必要,).

可悲的是,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).

其他推荐答案

感谢 @lukehutchinson的提示,我继续寻找更好的解决方案,并很高兴地报告说,实际上使用Reflection API有一种更简单的方法.您可以使用jni函数ToReflectedMethod将methodID转换为java.lang.reflect.Method或java.lang.reflect.Constructor,然后您可以分别调用invoke或newInstance,它将处理所有必要的拆箱转换.

这是一个概念证明,省略了错误检查以确保清晰度.

test.java:

public class test {
    static {
        System.loadLibrary("native");
    }
    
    public static void main(String[] args) {
        /* This is the constructor String(byte[], int, int).
           This call will print out BCD - the result of creating
           a string from bytes 1-3 of the array */
        System.out.println(new test().makeObject("java/lang/String", "([BII)V", new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45 }, 1, 3));
    }

    private native Object makeObject(String clazz, String signature, Object... args);
}

libnative.c:

#include <jni.h>

JNIEXPORT jobject JNICALL Java_test_makeObject(JNIEnv *env, jobject this, jstring clazzName, jstring signature, jobjectArray args) {
    const char *clazzNameStr = (*env)->GetStringUTFChars(env, clazzName, NULL);
    const char *signatureStr = (*env)->GetStringUTFChars(env, signature, NULL);

    jclass clazz = (*env)->FindClass(env, clazzNameStr);
    jmethodID methodID = (*env)->GetMethodID(env, clazz, "<init>", signatureStr);
    jobject reflectedMethod = (*env)->ToReflectedMethod(env, clazz, methodID, JNI_FALSE);

    jclass constructorClazz = (*env)->FindClass(env, "java/lang/reflect/Constructor");
    jmethodID newInstanceMethod = (*env)->GetMethodID(env, constructorClazz, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");

    jobject result = (*env)->CallObjectMethod(env, reflectedMethod, newInstanceMethod, args);

    (*env)->ReleaseStringUTFChars(env, clazzName, clazzNameStr);
    (*env)->ReleaseStringUTFChars(env, signature, signatureStr);
    return result;
}

以上所述是小编给大家介绍的调用jni jni带有参数的jni,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/26168.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。