0

I a Library in C that I'm leveraging for an Android application. This library has an audio stream that it occasionally flushes. When this happens it calls a write callback function of my design.

My intent is to have that C callback call a method on a specific Java Object which will handle stuff with the strem.

Currently I have code like so:

methodID compressionHandler=0;
jobject compressionHandlerClass;
int audioBufferChunkSize;    
static JavaVM *gJavaVM;

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    gJavaVM = vm;
    return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL
Java_com_my_code_init(JNIEnv* env, jobject obj, /*classpath of the class we want to call against*/jstring compressedAudioHandlerPath, /*class instance we want to call against*/jobject callbackClass) {
......
// this is a global ref as per:
    //http://stackoverflow.com/questions/14765776/jni-error-app-bug-accessed-stale-local-reference-0xbc00021-index-8-in-a-tabl
    compressionHandlerClass = (*env)->NewGlobalRef(env,callbackClass);
    // name of the class
    const char *classLocation;
    // convert jString to c String
    classLocation = (*env)->GetStringUTFChars( env, compressedAudioHandlerPath , NULL ) ;
    // tmp variable for holding the class location, relates to the above issue with garbage collection
    jclass clazz = (*env)->FindClass(env, classLocation);

    // the actual method that we want to call, this gets used in the writeCallback
    compressionHandler = (*env)->GetMethodID(env, clazz, "handleCompressedAudio", "([B)V");
    ......
}

The callback method looks like so:

void writeCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) {

JNIEnv *env;
int isAttached = 0;
if ((status = (*gJavaVM)->GetEnv(gJavaVM, (void**)&env, JNI_VERSION_1_6)) < 0) {
    if ((status = (*gJavaVM)->AttachCurrentThread(gJavaVM, &env, NULL)) < 0) {
            return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
    }
    isAttached = 1;
}
if(*env!=0 && compressionHandler!=0){
       jbyteArray arr = (*env)->NewByteArray(env,bytes);
       (*env)->SetByteArrayRegion(env,arr, 0, bytes, (jbyte*)buffer);
       (*env)->CallVoidMethod(env,compressionHandlerClass, compressionHandler,arr);
       free(arr);
       free(env);
       free(isAttached);
    }
}

I'm getting crashes at the CallVoidMethod, that signature of which is an interface implemented by whatever object I pass in:

public interface CompressedAudioHandler {
        void handleCompressedAudio(byte[] buff);
    }

I suspect that I am improperly attaining/keep references to these objects, but I haven't found a great way to handle that. Any advice on how I can more correctly handle this?

Nathaniel D. Waggoner
  • 2,856
  • 2
  • 19
  • 41
  • regarding the c side reference to the passed in obj you may want to read thru similiar stuff on audio stream interfaces ( #651 write buffer ) , see the casting parts where objects coming from java are cast to Cpp types... https://github.com/audioBoom/audioboo-android/blob/master/jni/jni/FLACStreamEncoder.cpp – Robert Rowntree Jan 02 '15 at 16:05
  • You shouldn't `free(arr)`, since it's a local reference to a Java byte array. Use `DeleteLocalRef` instead. Also, `env` should not be freed, and obviously not `isAttached` either since that's just an `int`. What you should do instead is call `DetachCurrentThread` if `isAttached` is true (1). – Michael Jan 02 '15 at 17:03
  • Thanks. Getting rid of the free() calls stopped the segfaults. – Nathaniel D. Waggoner Jan 02 '15 at 18:24

0 Answers0