JNI对数组赋值排序,创建数组并返回给Java

本文介绍如何使用JNI在C中操作Java数组,包括修改数组元素及排序,并从C中创建并返回Java数组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创建两个native方法

    //传递数组,操作后,返回
    public native void passArrayMethod(int[] arr);

    //创建指定长度数组
    public native int[] createArrayMethod(int len);

生成对应的c函数

JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_passArrayMethod
  (JNIEnv *, jobject, jintArray);

JNIEXPORT jintArray JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createArrayMethod
  (JNIEnv *, jobject, jint);

传递数组给JNI,修改第一个元素值,然后排序

int com(const void *a, const void *b){
    return *(int *)a - *(int *)b;//升序
}
JNIEXPORT void JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_passArrayMethod
        (JNIEnv *env, jobject jobj, jintArray jarr){
    //1.获取数组指针
    jint *arr = env->GetIntArrayElements(jarr, NULL);
    *arr = 100;
    //2.获取数组长度
    int len = env->GetArrayLength(jarr);
    //3.排序
    qsort(arr, len, sizeof(jint), com);

    //4.释放资源
    env->ReleaseIntArrayElements(jarr, arr, JNI_COMMIT);
//    env->ReleaseIntArrayElements(jarr, arr, JNI_ABORT);
    //  对于最后一个参数(如果指针指向的数组为副本时,否则该参数不起作用)
    //      0       copy back the content and free the elems buffer
    //      JNI_COMMIT      copy back the content but do not free the elems buffer
    //      JNI_ABORT       free the buffer without copying back the possible changes
};

JNI生成数组,并返回

JNIEXPORT jintArray JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createArrayMethod
        (JNIEnv *env, jobject jobj, jint len){
    //1.新建长度len数组
    jintArray jarr = env->NewIntArray(len);
    //2.获取数组指针
    jint *arr = env->GetIntArrayElements(jarr, NULL);
    //3.赋值
    int i = 0;
    for(; i < len; i++){
        arr[i] = i;
    }
    //4.释放资源
    env->ReleaseIntArrayElements(jarr, arr, 0);
    //5.返回数组
    return jarr;
};

MainActivity中调用

        int[] arr = {1, 3, 2, 6, 8, 0};
        Log.i(TAG, "arr修改前: " + getArrayString(arr));
        jd.passArrayMethod(arr);
        Log.i(TAG, "arr修改后: " + getArrayString(arr));

        Log.i(TAG, "------------------------------------------");

        int[] arr_new = jd.createArrayMethod(10);
        Log.i(TAG, "arr_new: "+ getArrayString(arr_new) );

输出结果:

09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: arr修改前: ,1,3,2,6,8,0
09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: arr修改后: ,0,2,3,6,8,100
09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: ------------------------------------------
09-26 17:02:29.454 994-994/com.test.git.jnidemo I/MainActivity-: arr_new: ,0,1,2,3,4,5,6,7,8,9
<think>嗯,用户的问题是关于JNI中如何将C++对象数组返回Java。这个问题很有技术深度,需要仔细梳理。用户可能正在开发JNI相关的跨语言交互功能,遇到对象数组传递的瓶颈。从ta引用的资料看,已经做过初步研究,但需要具体实现方案。用户真正的需求很明确:如何用C++生成对象数组后通过JNI完整传递到Java层,而不仅仅是基本类型数组。考虑到JNI对对象数组的特殊处理方式,核心难点在于两点:一是如何在C++端构造jobjectArray,二是如何封装C++对象到Java对象。用户可能是AndroidNDK开发者或Java中间件工程师,正在处理性能敏感的逻辑。ta引用中特别提到“没有直接转换方法”,说明已意识到这是个痛点。情绪上可能有些焦虑,毕竟这类问题容易卡住开发进度。我回忆起JNI确实禁止直接访问Java堆外内存中的对象数组。必须通过SetObjectArrayElement逐个填充元素,但这样会频繁触发JNI调用影响性能。另一种思路是先在C++构造所有对象,再一次性复制到数组里——但这要求对象结构稳定。技术上还需要注意数组维度问题。用户引用的资料[4]提到二维三维数组传递,但当前问题聚焦一维对象数组。考虑到后续扩展性,可以强调多维的处理思路类似。还有个隐形风险:如果C++对象的生命周期比数组短,返回Java后可能悬空指针。必须确保C++对象足够持久,或通过JNI全局引用保护。这个细节容易被忽略但极其关键。最后用户似乎更关注“方法”而非原理,所以回复应该以代码骨架为主,理论为辅。但安全提示不能省略,尤其是全局引用管理这块,否则容易导致内存泄漏。</think> 在JNI返回C++对象数组Java需要以下关键步骤(参考JNI规范及引用[1][3][4]): ### 核心实现流程 1. **创建Java对象数组** 在C++端使用`NewObjectArray`创建Java数组: ```c++ jclass clazz = env->FindClass("com/example/MyClass"); // 目标Java类 jobjectArray result = env->NewObjectArray( // 创建对象数组 arraySize, // 数组长度 clazz, // 元素类型 nullptr // 初始值(设为NULL) ); ``` 2. **封装C++对象到Java对象** 通过JNI方法将每个C++对象转为Java对象: ```c++ for (int i = 0; i < arraySize; i++) { jobject javaObj = ConvertToJavaObject(env, cppObj[i]); // 自定义转换函数 env->SetObjectArrayElement( // 填充数组元素 result, i, javaObj ); env->DeleteLocalRef(javaObj); // 释放局部引用(重要!) } ``` 3. **返回数组Java** 在JNI函数中直接返回创建的对象数组: ```c++ return result; // 返回Java层 ``` ### 关键细节说明 1. **对象转换要求** - `ConvertToJavaObject`需自行实现(见下文代码模板) - 必须将C++对象数据映射到Java对象的字段/方法 - 通过`env->NewObject`构造Java对象实例 2. **内存管理原则** - ✔️ 对每个创建Java对象使用`DeleteLocalRef`防止内存泄漏 - ❌ **禁止直接传递C++指针**到Java层(引用[3]) - 使用`SetObjectArrayElement`代替内存拷贝(引用[1][3]) 3. **代码示例** ```c++ // C++到Java对象转换模板 jobject ConvertToJavaObject(JNIEnv* env, CppClass& cppObj) { jclass jcls = env->FindClass("com/example/MyClass"); jmethodID constructor = env->GetMethodID(jcls, "<init>", "()V"); jobject jobj = env->NewObject(jcls, constructor); // 设置字段值(示例) jfieldID fid = env->GetFieldID(jcls, "data", "I"); env->SetIntField(jobj, fid, cppObj.value); return jobj; } ``` ### 性能优化建议 1. **缓存JNI标识符**(避免重复查找) ```c++ // 全局初始化时缓存 static jclass cachedClass = (jclass)env->NewGlobalRef(clazz); static jmethodID cachedConstructor = env->GetMethodID(cachedClass, "<init>", "()V"); ``` 2. **处理多维数组**(引用[4]) 二维数组需嵌套创建: ```c++ jobjectArray jarray = env->NewObjectArray(dim1, env->FindClass("[Ljava/lang/Object;"), NULL); for(int i=0; i<dim1; i++) { jobjectArray inner = env->NewObjectArray(dim2, targetClass, NULL); env->SetObjectArrayElement(jarray, i, inner); } ``` 3. **使用全局引用** 若需长期持有Java对象: ```c++ jobject globalObj = env->NewGlobalRef(localObj); env->DeleteLocalRef(localObj); // 删除局部引用 ``` > **重要安全提示**:必须显式管理JNI引用(局部/全局),否则会导致内存泄漏或JVM崩溃(引用[1][3])。跨线程操作需使用`AttachCurrentThread`获取有效JNIEnv指针[^1][^3][^4]。 --- ### 相关问题 1. 如何在C++中直接修改Java对象数组的元素而不返回整个数组? 2. JNI处理对象数组时如何避免内存泄漏? 3. 多线程环境下返回对象数组需要注意什么? 4. 如何传递包含复杂继承关系的C++对象数组Java? 5. JNI能否直接操作Java对象数组的底层内存?[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值