在 JNI 中,可以将 C 整数参数传递给 Java 原始 int 吗?
In JNI, can you pass a C integer parameter to a Java primitive int?
我有一个 Java 方法希望从某些本地方法调用:
public void onDownloadProgress(int current, int total) {
}
我正在尝试从本机方法调用上述 Java 方法:
int current = ...
int total = ...
jni_callVoidMethod(
env,
jDownloadCallback,
"onDownloadProgress",
"(II)V",
current,
total
);
jni_callVoidMethod是一个辅助方法,它的实现是:
void jni_callVoidMethod(JNIEnv *env, jobject receiver, const char *methodName, const char *contract, ...) {
jclass clazz = env->GetObjectClass(receiver);
if (NULL != clazz) {
jmethodID method = env->GetMethodID(
clazz,
methodName,
contract
);
if (NULL != method) {
BUILD_VARARGS()
env->CallVoidMethodV(
receiver,
method,
va_args
);
if (env->ExceptionCheck()) { // prints out the exception if one is present
env->ExceptionDescribe();
env->ExceptionClear();
}
va_end(va_args);
}
env->DeleteLocalRef(clazz);
}
}
但是,在 Java 方法中,我得到了一些非常奇怪的 int 值。比如3840120912,请问能不能直接把一个C的int转成一个Java原始int,而不是在Java方法中声明参数变成Integer类型?将这两个参数声明为 Integer 对我有用,我得到了正确的值。
编辑:
BUILD_VARARGS宏的实现:
#define BUILD_VARARGS() \
va_list va_args; \
va_start(va_args, contract); \
const char *cur = contract; \
while ('[=14=]' != *cur && '(' != *cur) { /* skip to opening paren */ \
cur++; \
} \
while ('[=14=]' != *cur && ')' != *cur) { /* stop at closing paren */ \
switch (*cur) { \
case 'Z': \
va_arg(va_args, int); /* bool (unsigned 8-bit int) */ \
break; \
case 'B': \
va_arg(va_args, int); /* byte (signed 8-bit int) */ \
break; \
case 'C': \
va_arg(va_args, int); /* char (unsigned 16-bit int) */ \
break; \
case 'S': \
va_arg(va_args, int); /* short (signed 16-bit int) */ \
break; \
case 'I': \
va_arg(va_args, long long); /* int (signed 32-bit int) (must be passed in as a long long) */ \
break; \
case 'J': \
va_arg(va_args, long); /* long (signed 64-bit int) */ \
break; \
case 'F': \
va_arg(va_args, double); /* float (32 bits) */ \
break; \
case 'D': \
va_arg(va_args, double); /* double (64 bits) */ \
break; \
case 'L': \
/* fully-qualified-class */ \
while (';' != *++cur && '[=14=]' != *cur); /* advance to end of class declaration */ \
/* FIXME breaks varargs, seems to not be needed va_arg(va_args, jobject); */ \
break; \
case '[': \
/* TODO type type[] */ \
case '(': \
/* TODO ( arg-types ) ret-type method type */ \
default: \
break; \
} \
cur++; \
}
这说明了一切!您对 va_arg
的调用消耗了参数,当 CallVoidMethodV
到达它时,它正在从堆栈中的其他地方读取。来自 va_arg
手册:
The va_arg() macro expands to an expression that has the type and value
of the next argument in the call. The parameter ap is the va_list ap
initialized by va_start().
Each call to va_arg() modifies ap so that the
next call returns the next argument.
相反,您应该创建 va_list
并立即将其交给 CallVoidMethodV
:
va_list va_args;
va_start(va_args, contract);
env->CallVoidMethodV(receiver, method, va_args);
va_end(va_args);
我有一个 Java 方法希望从某些本地方法调用:
public void onDownloadProgress(int current, int total) {
}
我正在尝试从本机方法调用上述 Java 方法:
int current = ...
int total = ...
jni_callVoidMethod(
env,
jDownloadCallback,
"onDownloadProgress",
"(II)V",
current,
total
);
jni_callVoidMethod是一个辅助方法,它的实现是:
void jni_callVoidMethod(JNIEnv *env, jobject receiver, const char *methodName, const char *contract, ...) {
jclass clazz = env->GetObjectClass(receiver);
if (NULL != clazz) {
jmethodID method = env->GetMethodID(
clazz,
methodName,
contract
);
if (NULL != method) {
BUILD_VARARGS()
env->CallVoidMethodV(
receiver,
method,
va_args
);
if (env->ExceptionCheck()) { // prints out the exception if one is present
env->ExceptionDescribe();
env->ExceptionClear();
}
va_end(va_args);
}
env->DeleteLocalRef(clazz);
}
}
但是,在 Java 方法中,我得到了一些非常奇怪的 int 值。比如3840120912,请问能不能直接把一个C的int转成一个Java原始int,而不是在Java方法中声明参数变成Integer类型?将这两个参数声明为 Integer 对我有用,我得到了正确的值。
编辑: BUILD_VARARGS宏的实现:
#define BUILD_VARARGS() \
va_list va_args; \
va_start(va_args, contract); \
const char *cur = contract; \
while ('[=14=]' != *cur && '(' != *cur) { /* skip to opening paren */ \
cur++; \
} \
while ('[=14=]' != *cur && ')' != *cur) { /* stop at closing paren */ \
switch (*cur) { \
case 'Z': \
va_arg(va_args, int); /* bool (unsigned 8-bit int) */ \
break; \
case 'B': \
va_arg(va_args, int); /* byte (signed 8-bit int) */ \
break; \
case 'C': \
va_arg(va_args, int); /* char (unsigned 16-bit int) */ \
break; \
case 'S': \
va_arg(va_args, int); /* short (signed 16-bit int) */ \
break; \
case 'I': \
va_arg(va_args, long long); /* int (signed 32-bit int) (must be passed in as a long long) */ \
break; \
case 'J': \
va_arg(va_args, long); /* long (signed 64-bit int) */ \
break; \
case 'F': \
va_arg(va_args, double); /* float (32 bits) */ \
break; \
case 'D': \
va_arg(va_args, double); /* double (64 bits) */ \
break; \
case 'L': \
/* fully-qualified-class */ \
while (';' != *++cur && '[=14=]' != *cur); /* advance to end of class declaration */ \
/* FIXME breaks varargs, seems to not be needed va_arg(va_args, jobject); */ \
break; \
case '[': \
/* TODO type type[] */ \
case '(': \
/* TODO ( arg-types ) ret-type method type */ \
default: \
break; \
} \
cur++; \
}
这说明了一切!您对 va_arg
的调用消耗了参数,当 CallVoidMethodV
到达它时,它正在从堆栈中的其他地方读取。来自 va_arg
手册:
The va_arg() macro expands to an expression that has the type and value of the next argument in the call. The parameter ap is the va_list ap initialized by va_start().
Each call to va_arg() modifies ap so that the next call returns the next argument.
相反,您应该创建 va_list
并立即将其交给 CallVoidMethodV
:
va_list va_args;
va_start(va_args, contract);
env->CallVoidMethodV(receiver, method, va_args);
va_end(va_args);