1

In fact I have this working, just not correctly. I have a callback (posted in a previous question: Unable to get JNIEnv* value in arbitrary context), which now calls a callback in the Java layer... The only problem is the datatype that the callback returns. In my Java code, when debugging, I see that the type of what the callback passes to Java is of the type class [B instead of the type byte[] what I expect.

The only thing that the Java callback does is place it in a Queue, but the problem arises when I need to process that Queue.

The callback in Java:

public void enqueueAudio(byte[] audioData){
    if(audioData != null){
        mWriteQueue.offer(audioData);
    }
}

Processing of the Queue:

private void writeToFile(String file){
    int totalNumOfBytes = 0;
    byte[] dataFromQueue = new byte[0];
    byte[] temp;
    for(byte[] data : mWriteQueue){
        temp = dataFromQueue;
        dataFromQueue = new byte[temp.length + data.length];
        System.arraycopy(temp, 0, dataFromQueue, 0, temp.length);
        System.arraycopy(data, 0, dataFromQueue, temp.length, data.length);
        totalNumOfBytes += data.length;
    }
    // Write the total byte[] to the specified file.
    mFileHandler.write(file, dataFromQueue);
    updateUI("Number of bytes written to " + file + " : " + totalNumOfBytes + "\n");
}

As you can see, I assume that the Queue is filled with byte[]'s, and not with class [B's, resulting in class cast exceptions...

So, is it correct that the native code returns a class [B instead of a byte[]?

For the sake of completeness, here is the method in C:

void recorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
    SLresult result;
    JNIEnv* env;
    jbyteArray data;
    (*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
    if(env == NULL){
        LOG_ERROR("Could not get JNIEnv*");
        return;
    }
    data = (*env)->NewByteArray(env, MAX_PACKET_SIZE);
    if(data == NULL){
        LOG_ERROR("No memory could be allocated for buffer");
        return;
    }
    (*env)->SetByteArrayRegion(env, data, 0, MAX_PACKET_SIZE, recorderBuffer);
    (*env)->CallByteMethodA(env, javaObject, javaCallbackMID, data);
    (*env)->DeleteLocalRef(env, data);
    result = (*bq)->Enqueue(bq, recorderBuffer,
                            RECORDER_FRAMES * sizeof(jbyte));
    checkError(result, "Unable to enqueue new buffer");
    (*javaVM)->DetachCurrentThread(javaVM);
}
Community
  • 1
  • 1
ThaMe90
  • 4,196
  • 5
  • 39
  • 64

1 Answers1

3

In fact I have this working, just not correctly.

That's a contradiction in terms.

  (*env)->CallByteMethodA(env, javaObject, javaCallbackMID, data);

It's not a ByteMethod. It's a VoidMethod.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • The javaCallbackMID is the method id of enqueueAudio in Java, which was retrieved by the call `javaCallbackMID = (*env)->GetMethodID(env, javaClass, "enqueueAudio", "([B)V");`. I only use it to call the Java method with a byte[] parameter, which is not what is passed into it when it is called... – ThaMe90 May 16 '11 at 08:27
  • However, this still doesn't solve the issue... The problem is not that the Java method isn't called, it is called with the wrong parameter type... – ThaMe90 May 16 '11 at 08:28
  • *Sigh* I really dislike this way of programming, combining 2 languages that is, but I found out you were indeed correct... I forgot to remove the capital letter A from `CallByteMethodA` when I changed it to `CallVoidMethod`. It works now, so thank you.. :) – ThaMe90 May 16 '11 at 08:35