JNI中从C char数组创建jshortArray的正确性及编解码交互疑问
问题1:char数组转jshortArray的代码正确性及内容一致性
你提供的代码存在内存越界和内容逻辑错误,生成的jshortArray不会和原char数组内容一致:
- 内存越界问题:
NewShortArray(s*2)创建了包含s*2个short元素的数组,总占用字节数是4*s(每个short占2字节),但你分配的char数组只有s字节。调用SetShortArrayRegion时会从b指针读取s*2*2=4s字节,远超内存分配范围,会触发未定义行为(比如崩溃、垃圾数据)。 - 内容不一致问题:将
char*强制转换为short const*后,系统会按当前字节序(大端/小端)把连续两个char字节拼接成一个short。比如原char数组是[0x01, 0x02, 0x03, 0x04],小端系统下会被解析为[0x0201, 0x0403],完全不是原char数组的内容。
如果需要把char数组的每个字节单独转为short(比如8位转16位),正确做法是创建长度为s的jshortArray,循环将每个char值赋值给对应的short元素;如果原char数组是16位PCM的字节流,应该创建长度为s/2的jshortArray,并按正确字节序拼接两个char为一个short。
问题2:libspx解码输出jshortArray的尺寸设置
基于libspx(speex)的解码逻辑,输出jshortArray的尺寸需要匹配解码后的PCM样本数,而非输入字节数的固定倍数:
- 首先通过libspx的API获取单帧样本数:调用
speex_decoder_ctl(decoder, SPEEX_GET_FRAME_SIZE, &frame_size),其中frame_size就是每帧解码后输出的short样本数(窄带默认160,宽带默认320)。 - 根据输入压缩数据的总帧数,计算总样本数
total_samples = frame_count * frame_size,以此作为jshortArray的长度。 - 注意:如果输入包含多帧,需要确保输出数组长度足够容纳所有解码样本;若Java层预先分配数组,需让其长度不小于计算出的
total_samples,避免截断。
问题3:Java值传递与JNI数组操作的机制
Java数组的传递逻辑
Java确实是按值传递,但数组是对象,传递的是对象引用的副本。也就是说,native方法拿到的是Java数组对象的引用副本,指向的还是堆上同一个数组实例。因此你在native层修改数组内容后,Java层可以直接获取到修改后的结果。
JNI与C/C++数组操作的差异
- C/C++中直接通过指针操作内存,数组本质是连续内存块的指针,传递指针就能直接修改原数组内容。
- JNI中不能直接用指针操作Java数组(因为Java有垃圾回收,可能会移动数组内存),必须通过JNIEnv提供的安全函数:
- 读取输入byte[]:用
GetByteArrayElements获取数组的内存指针,使用完后调用ReleaseByteArrayElements释放。 - 写入输出short[]:可以用
SetShortArrayRegion直接写入指定区域,或者用GetShortArrayElements获取可修改的指针,写入后调用ReleaseShortArrayElements同步到Java堆。
- 读取输入byte[]:用
针对你的native方法nDecodeBuffer的说明
你传入的short[] sArr是Java预先分配的数组,只要其长度足够容纳解码结果,在native层通过JNI函数写入数据后,Java层就能直接访问填充好的内容,不需要额外返回操作。
内容的提问来源于stack exchange,提问作者dev
相关产品推荐
相关产品推荐

