GetByteArrayRegion导致ArrayIndexOutOfBoundsException

Moh*_*hin 3 android memory-leaks speex android-ndk

我正在尝试使用Speex库解码Speex编码的音频文件,但我总是得到 ArrayIndexOutOfBoundsException

JNIEXPORT jint JNICALL Java_com_app_shared_Speex_decode
    (JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {

        jbyte buffer[dec_frame_size ];
        jshort output_buffer[dec_frame_size ];
        jsize encoded_length = size;

        if (!codec_open)
            return 0;

        env->GetByteArrayRegion(encoded, rtp_header, encoded_length, buffer);

        /*  
        jboolean isCopy;
        jbyte* data = env->GetByteArrayElements(encoded, &isCopy);
        if(isCopy){
        memcpy(buffer, data, encoded_length);               
        env->ReleaseByteArrayElements(encoded, data, JNI_ABORT);
        }
        */

        speex_bits_read_from(&dbits, (char *)buffer, encoded_length);
        speex_decode_int(dec_state, &dbits, output_buffer);

        std::string bufStr;

        for(int i=0; i<20; i++){
            char buff[100];
            sprintf(buff, "Speex test %d => %d", i, output_buffer[i]);
            bufStr = buff;
            LOGI(bufStr.c_str());
        }

        bufStr = "before SetShortArrayRegion";
        LOGI(bufStr.c_str());

        (env)->SetShortArrayRegion(lin, rtp_header, dec_frame_size, output_buffer);

        bufStr = "after SetShortArrayRegion";
        LOGI(bufStr.c_str());

        return (jint)dec_frame_size;            
}
Run Code Online (Sandbox Code Playgroud)

我明白了:

SharedSpeex before SetShortArrayRegion
dalvikvm    JNI WARNING: JNI method called with exception pending
dalvikvm              in Lcom/company/shared/Speex;.decode:([B[SI)I (SetShortArrayRegion)
dalvikvm    Pending exception is:
dalvikvm    java.lang.ArrayIndexOutOfBoundsException: [B offset=12 length=1634 src.length=1634
Run Code Online (Sandbox Code Playgroud)

有趣的是,如果我对包含的行进行完全注释SetShortArrayRegion,则在退出范围时会发生异常:

SharedSpeex       before SetShortArrayRegion
SharedSpeex       after SetShortArrayRegion
AndroidRuntime    Shutting down VM
dalvikvm          threadid=1: thread exiting with uncaught exception (group=0xa62ca288)
AndroidRuntime    FATAL EXCEPTION: main
AndroidRuntime    java.lang.ArrayIndexOutOfBoundsException: [B offset=12 length=1634 src.length=1634
Run Code Online (Sandbox Code Playgroud)

在官方JNI文档中,它显然更喜欢GetByteArrayRegion而不是GetByteArrayElements因为:

  • 降低程序员错误的风险 - 没有在发生故障后忘记调用Release的风险.

fad*_*den 5

GetByteArrayRegion检查数组的边界,以确保您没有运行到最后. GetByteArrayElements只是得到一个指向数组开头的指针,所以你memcpy()可以自由地在结束时运行而不被注意到.

超出的原因在例外情况中注明:

[B offset=12 length=1634 src.length=1634
Run Code Online (Sandbox Code Playgroud)

[B手段byte[].数组的长度与请求的长度匹配(1634),但它从数组的偏移量12个字节开始.所以你试图在结束时读取12个字节.

从Java代码抛出的异常不会中断您的C代码; VM只在当前线程上设置一个标志.您必须显式地测试它们ExceptionCheck,并清除它们或返回到VM将继续抛出过程的Java代码.如果您启用了CheckJNI,并且VM看到您在发出异常时进行了少量调用,则会出现"JNI警告"并且VM中止.