You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java/JNI技术疑问:直接返回jstring作为jobject给Java是否可行?

JNI绑定SANE时nativeGetCurrentValue调用崩溃问题

我通过JNI实现了SANE与Java的绑定,但调用nativeGetCurrentValue函数时出现崩溃。

Java端代码

native Object nativeGetCurrentValue(long hnd, int num, int type);

Native端代码

JNIEXPORT jobject JNICALL Java_package_Sane_nativeGetCurrentValue(JNIEnv * env, jobject o, jlong hnd, jint num, jint type) {
    void *value;


    if (sane_control_option((SANE_Handle *) hnd, num, SANE_ACTION_GET_VALUE, value, NULL) == SANE_STATUS_GOOD) {
        switch (type) {
            case SANE_TYPE_BOOL: {
//                fprintf(stderr, "SANE_TYPE_BOOL...\n");
                jboolean * v = (jboolean *)value;

                jclass cls = env->FindClass("java/lang/Boolean");
                jmethodID midBool = env->GetMethodID(cls, "<init>", "(Z)V");
                return env->NewObject(cls, midBool, *v);
            }
            case SANE_TYPE_FIXED: {
//                fprintf(stderr, "SANE_TYPE_FIXED...\n");
                SANE_Word * v = (SANE_Word *)value;
                double d = SANE_UNFIX(*v);

                jclass cls = env->FindClass("java/lang/Double");
                jmethodID midDoub = env->GetMethodID(cls, "<init>", "(D)V");
                return env->NewObject(cls, midDoub, (jdouble)d);
            }
            case SANE_TYPE_INT: {
//                fprintf(stderr, "SANE_TYPE_INT...\n");
                int * v = (int *)value;

                jclass cls = env->FindClass("java/lang/Integer");
                jmethodID midInit = env->GetMethodID(cls, "<init>", "(I)V");
                return env->NewObject(cls, midInit, *v);
            }
            case SANE_TYPE_STRING:
//                fprintf(stderr, "SANE_TYPE_STRING...\n");
                jstring str = env->NewStringUTF((char *)value);
                return str;
        }
    }

    return NULL;
}

我怀疑自己的实现存在问题,想咨询:使用env->NewStringUTF创建的jstring直接返回给Java是否可行?


问题解答

关于jstring直接返回的可行性

完全可以直接返回env->NewStringUTF创建的jstring给Java,这是符合JNI规范的操作。JNI创建的字符串对象会被Java虚拟机的垃圾回收机制管理,不需要额外手动释放,这部分你的写法没问题。

崩溃的真正原因

你的代码里有个致命问题:void *value;是未初始化的野指针,直接传给sane_control_option的第四个参数会导致内存访问错误,这才是崩溃的根源。

根据SANE文档,sane_control_option的第四个参数是输出参数,需要你预先分配好对应类型的内存空间,或者传入指向有效内存的指针,让函数把值写入进去。你现在传的是未初始化的value,函数会尝试往随机内存地址写数据,必然触发崩溃。

修复方案

针对不同的SANE类型,预先分配对应大小的内存:

JNIEXPORT jobject JNICALL Java_package_Sane_nativeGetCurrentValue(JNIEnv * env, jobject o, jlong hnd, jint num, jint type) {
    // 根据类型分配对应内存
    switch (type) {
        case SANE_TYPE_BOOL: {
            SANE_Bool bool_val;
            if (sane_control_option((SANE_Handle)hnd, num, SANE_ACTION_GET_VALUE, &bool_val, NULL) == SANE_STATUS_GOOD) {
                jclass cls = env->FindClass("java/lang/Boolean");
                jmethodID midBool = env->GetMethodID(cls, "<init>", "(Z)V");
                return env->NewObject(cls, midBool, (jboolean)bool_val);
            }
            break;
        }
        case SANE_TYPE_FIXED: {
            SANE_Word fixed_val;
            if (sane_control_option((SANE_Handle)hnd, num, SANE_ACTION_GET_VALUE, &fixed_val, NULL) == SANE_STATUS_GOOD) {
                double d = SANE_UNFIX(fixed_val);
                jclass cls = env->FindClass("java/lang/Double");
                jmethodID midDoub = env->GetMethodID(cls, "<init>", "(D)V");
                return env->NewObject(cls, midDoub, (jdouble)d);
            }
            break;
        }
        case SANE_TYPE_INT: {
            SANE_Word int_val; // SANE的int类型实际是SANE_Word
            if (sane_control_option((SANE_Handle)hnd, num, SANE_ACTION_GET_VALUE, &int_val, NULL) == SANE_STATUS_GOOD) {
                jclass cls = env->FindClass("java/lang/Integer");
                jmethodID midInit = env->GetMethodID(cls, "<init>", "(I)V");
                return env->NewObject(cls, midInit, (jint)int_val);
            }
            break;
        }
        case SANE_TYPE_STRING: {
            // 字符串建议先获取所需长度再动态分配,这里先用固定缓冲区示例
            char str_buf[1024];
            if (sane_control_option((SANE_Handle)hnd, num, SANE_ACTION_GET_VALUE, str_buf, NULL) == SANE_STATUS_GOOD) {
                return env->NewStringUTF(str_buf);
            }
            break;
        }
    }

    return NULL;
}

额外注意事项

  1. SANE_Handle本身就是指针类型,不需要强转成(SANE_Handle *),直接用(SANE_Handle)hnd即可。
  2. 字符串类型最好先通过sane_control_option获取缓冲区大小,再动态分配内存,避免固定缓冲区不够用。
  3. 可以缓存FindClass和GetMethodID的结果,减少重复调用的性能开销。

内容的提问来源于stack exchange,提问作者Thiago Sayão

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 11:25:21