簡體   English   中英

在 JNI 代碼中將 `jobjectArray` 拆箱到 `jvalue[]` 數組中

[英]Unboxing a `jobjectArray` into a `jvalue[]` array in JNI code

我有以下 Java 本機方法聲明:

static native Object nativeCallObjectMethod
        (Object object, Method method, Object... params);

這會產生以下 C function 聲明(請注意Object... params映射到jobjectArray params ):

JNIEXPORT jobject JNICALL Java_pkg_Cls_nativeCallObjectMethod
        (JNIEnv *env, jclass ignored, jobject obj, jobject method,
                jobjectArray params) { }

我想調用params為 arguments 的method

    jmethodID methodID = (*env)->FromReflectedMethod(env, method);
    return (*env)->CallObjectMethod(env, obj, methodID, params);

但是, paramsjobjectArray類型,而 JNI 方法callObjectMethod*只能采用以下三個 forms 之一:

  1. CallObjectMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...)
  2. CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args)
  3. CallObjectMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args)

這種方法沒有任何形式在最后一個參數 position 中采用jobjectArray 。我假設方法 2 是我要調用的方法,並且我意識到jobjectArray元素可能需要拆箱以生成一個jvalue值數組。 我發現了關於如何實現這個的建議: https://stackoverflow.com/a/30961708/3950982

但是,我不敢相信沒有標准的方法可以使用 JNI API 將jobjectArrayjvalue數組中。當然必須有一種內置的方法來實現這一點,因為 Java 在使用反射時會在內部執行此操作嗎?

我該如何解決這個問題,最好不要重新發明輪子?

由於您已經有一個reflect.Method ,您可以直接.invoke它。 這已經為你做了拆箱:

jclass clsMethod = env->GetObjectClass(method);
jmethodID midMethodInvoke = env->GetMethodID(clsMethod, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
jobject result = env->CallObjectMethod(method, midMethodInvoke, obj, params);

對於未來的讀者:如果你只有一個jclass + jmethodID ,我會推薦使用ToReflectedMethod來獲得一個reflect.Method然后像上面那樣調用.invoke來省去你自己的麻煩。

我寫了一個將jobjectArray拆箱到jvalue[]數組中的方法:

// Unbox a jobjectArray of method invocation args into a jvalue array. Returns 0 if an exception was thrown, or 1 if unboxing succeeded.
int unbox(JNIEnv *env, jobject method, jobjectArray args, jsize num_args, jvalue* arg_jvalues) {
    // Get parameter types of method
    jclass methodClass = (*env)->GetObjectClass(env, method);
    jmethodID getParameterTypesMethodID = (*env)->GetMethodID(env, methodClass, "getParameterTypes", "()[Ljava/lang/Class;");
    jobject parameterTypes = (*env)->CallObjectMethod(env, method, getParameterTypesMethodID);
    jsize num_params = (*env)->GetArrayLength(env, parameterTypes);

    // Check arg arity
    if (num_args != num_params) {
        throwIllegalArgumentException(env, "Tried to invoke method with wrong number of arguments");
        return 0;
    }
    
    // Unbox args
    for (jsize i = 0; i < num_args; i++) {
        jobject paramType = (*env)->GetObjectArrayElement(env, parameterTypes, i);
        jobject arg = (*env)->GetObjectArrayElement(env, args, i);
        jclass argType = arg == NULL ? (jclass) NULL : (*env)->GetObjectClass(env, arg);

        if ((*env)->IsSameObject(env, paramType, int_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Integer");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Integer_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Integer");
                return 0;
            } else {
                arg_jvalues[i].i = (*env)->CallIntMethod(env, arg, int_value_methodID);
            }
        } else if ((*env)->IsSameObject(env, paramType, long_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Long");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Long_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Long");
                return 0;
            } else {
                arg_jvalues[i].j = (*env)->CallLongMethod(env, arg, long_value_methodID);
            }
        } else if ((*env)->IsSameObject(env, paramType, short_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Short");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Short_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Short");
                return 0;
            } else {
                arg_jvalues[i].s = (*env)->CallShortMethod(env, arg, short_value_methodID);
            }
        } else if ((*env)->IsSameObject(env, paramType, char_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Character");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Character_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Character");
                return 0;
            } else {
                arg_jvalues[i].c = (*env)->CallCharMethod(env, arg, char_value_methodID);
            }
        } else if ((*env)->IsSameObject(env, paramType, boolean_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Boolean");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Boolean_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Boolean");
                return 0;
            } else {
                arg_jvalues[i].z = (*env)->CallBooleanMethod(env, arg, boolean_value_methodID);
            }
        } else if ((*env)->IsSameObject(env, paramType, byte_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Byte");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Byte_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Byte");
                return 0;
            } else {
                arg_jvalues[i].b = (*env)->CallByteMethod(env, arg, byte_value_methodID);
            }
        } else if ((*env)->IsSameObject(env, paramType, float_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Float");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Float_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Float");
                return 0;
            } else {
                arg_jvalues[i].f = (*env)->CallFloatMethod(env, arg, float_value_methodID);
            }
        } else if ((*env)->IsSameObject(env, paramType, double_class)) {
            if (arg == NULL) {
                throwIllegalArgumentException(env, "Tried to unbox a null argument; expected Double");
                return 0;
            } else if (!(*env)->IsSameObject(env, argType, Double_class)) {
                throwIllegalArgumentException(env, "Tried to unbox arg of wrong type; expected Double");
                return 0;
            } else {
                arg_jvalues[i].d = (*env)->CallDoubleMethod(env, arg, double_value_methodID);
            }
        } else {
            // Arg does not need unboxing, but we need to check if it is assignable from the parameter type
            if (arg != NULL && !(*env)->IsAssignableFrom(env, argType, paramType)) {
                throwIllegalArgumentException(env, "Tried to invoke function with arg of incompatible type");
                return 0;
            } else {
                arg_jvalues[i].l = arg;
            }
        }
    }
    return 1;
}

為了提高速度,在初始化時按如下方式獲得對Integer.classint.class類的全局引用:

jclass Integer_class = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/Integer"));
jclass int_class = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, Integer_class, (*env)->GetStaticFieldID(env, Integer_class, "TYPE", "Ljava/lang/Class;")));
jmethodID int_value_methodID = (*env)->GetMethodID(env, Integer_class, "intValue", "()I");

像這樣調用拆箱方法:

JNIEXPORT jint JNICALL Java_narcissus_Narcissus_callIntMethod(JNIEnv *env, jclass ignored, jobject obj, jobject method, jobjectArray args) {
    jmethodID methodID = (*env)->FromReflectedMethod(env, method);
    jsize num_args = (*env)->GetArrayLength(env, args);
    if (num_args == 0) {
        return (*env)->CallIntMethod(env, obj, methodID);
    }
    jvalue arg_jvalues[num_args];
    return unbox(env, method, args, num_args, arg_jvalues) ? (*env)->CallIntMethodA(env, obj, methodID, arg_jvalues) : (jint) 0;
}

上面的代碼不處理可變參數。 可以在Narcissus 庫中看到處理可變參數的完整版本(我是作者)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM