how to write and read from bytebuffer passing from java to jni

13,858

I don't use jni_onload, so no javah in C++

It may be irrelevant to your question, but you sort of have to do one or the other. You either need to do your Java-to-Native method mapping in the onLoad function or use javah to generate the method signatures that JNI can pick up at runtime. Otherwise your native methods will never be invoked.

If your native method is properly being called, however, whatever you are doing is probably fine.

I am stuck here, can I do double dbuf? or is it has to be a char?

The return value of the function is actually a void*, so you can cast it to whatever pointer type you want since it is just a memory address. However, the actual data type of the direct buffer will be jbyte, which is a typedef of signed char, so the following is probably best:

jbyte *dBuf = env->GetDirectBufferAddress(env, buf);

Let said I want to write 1,2,3,4,5 to this dbuf, how do I do it?

The notation in C/C++ to access values in an array is with [] notations, i.e.:

jbyte *dBuf = env->GetDirectBufferAddress(env, buf);

dBuf[0] = 63;
dBuf[1] = 127;
// ...and so on...

and after populating it, do I just call bb.get(position) in java?

Yes, you can use the accessor methods on ByteBuffer to access the data you have written from your native code.

Share:
13,858
Lan Nguyen
Author by

Lan Nguyen

Updated on June 19, 2022

Comments

  • Lan Nguyen
    Lan Nguyen almost 2 years

    I need help with my android project. I want to pass a buffer from java to jni, and my C++ code will populate the data. Then java will display them on the screen. I am not familiar much with C++ and have no idea how to write to write to the buffer.

    this is what i got. in java

    ByteBuffer bb = ByteBuffer.allocateDirect(216);
            IssmJni.processBuffer(bb);
    

    native method

    public static native void processBuffer(ByteBuffer bb);
    

    I don't use jni_onload, so no javah in C++

    static void fillBuffer(JNIEnv *env, jclass clazz, jobject buf)
        {
            double *dBuf = env->GetDirectBufferAddress(env, buf);
    
        }
    

    I am stuck here, can I do double dbuf? or is it has to be a char?

    Let said I want to write 1,2,3,4,5 to this dbuf, how do I do it? i am thinking of dbuf.put(1); ... dbuf.put(5) but it is not working. and after populating it, do I just call bb.get(position) in java?

    Someone clarify this for me please, example would be appreciated Thank you

    this is my table of native methods

    static JNINativeMethod method_table[] = {

    {"fac"      ,     "(J)J" , (void *) factorial},
        {"getBuffer",     "()[D" , (void *) getBufferNative},
        //{"processBuffer", "(Ljava/nio/ByteBuffer)V", (void *) fillBuffer}};
    

    The other two works fine except the last one.

  • Lan Nguyen
    Lan Nguyen over 11 years
    ty, it works,but I have 2 questions. First, as far as I know this is shared memory right. The reason that I am doing this is because the data will be huge and I do not want to marshaling the data every time passing back and forth. Second how do i make this jni_onload, I am using javah right now. For onload I tried this, {"processBuffer", "(Ljava/nio/ByteBuffer)V", (void *) fillBuffer} , processBuffer is native method from java, Ljava/nio/ByteBuffer this is the parameter bytebuffer, V refer to void, fillBuffer refers to C++ function. But it not mapping correctly, can you check it please, thanks
  • devunwired
    devunwired over 11 years
    Yes, the direct buffer is accessed on both sides without copying memory; that is why you don't have to pass the object back from native code.
  • Lan Nguyen
    Lan Nguyen over 11 years
    thanks, very informative. What about the second question Devu, can you see what I did wrong? thanks
  • devunwired
    devunwired over 11 years
    I think your method signature should be "(Ljava/nio/ByteBuffer;)V" instead...with a semicolon after the class name. The rest looks correct to me.
  • Lan Nguyen
    Lan Nguyen over 11 years
    If you look at the table I just did above the other two method doesn't have semicolon so, it is kinda weird isn't it. Yes the 2 fac, and getbuffer work perfectly fine. One more thing, I did this dBuf[0] = 10.67; and in java it returns 1.300779E-258. How come, I use bb.getDouble().
  • devunwired
    devunwired over 11 years
    The semicolon is only required on qualified Java class names, not primitives. Have a look here: dev.kanngard.net/Permalinks/ID_20050509144235.html
  • devunwired
    devunwired over 11 years
    The conversion issue is with types. Your buffer is an array of bytes, and it takes eight bytes (64 bits) to make up a double. So when you call getDouble() it takes the data from dBuf[n] through dBuf[n+7] to construct the value. You can read more about floating point types here: docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2‌​.3 I'm not a C++ expert, you may be able to cast the buffer return as a (double*) for easier insertion. Otherwise you will need to convert each double to bytes in native code first.
  • Lan Nguyen
    Lan Nguyen over 11 years
    DoubleBuffer buff = ByteBuffer.allocateDirect(15*8).asDoubleBuffer(); this works perfectly Devu, and putting semicolon works too thanks alot. Devu if you happen to know about native thread in jni please shoot me the link. I want to have a method from java that fire a native thread in JNI and communicate between main java thread and jni thread. Example would be awesome
  • devunwired
    devunwired over 11 years
    I'd post a new question for that because it's a bit outside my expertise.
  • Lan Nguyen
    Lan Nguyen over 11 years
    Hi Devu, I have this new problem, don't know if you can help. let say I have a C++ code that create class call hello and in that class has method called print and return string helloworld. If the UI side hit a button, it will called this print method, but I want to create only 1 object hello, and use the same print method instead of creating new object hello every time. I know I need to save the object as global reference, but how would you reference to it next call, if you know can you give me example code. Thanks,
  • devunwired
    devunwired over 11 years
    I think you should post these as new questions so others with experience will see them.
  • Lan Nguyen
    Lan Nguyen over 11 years
    I solved the previous one, ty tho. I am having this idea, but not sure if it is possible. Instead of passing in a DoubleBuffer. I want to pass in a DoubleBuffer[]. Jni side still does the data allocation. Will this be possible? can I say jdouble *dBuf = env->GetDirectBufferAddress(env, buf[i]) where i is the index of the array, or jdouble **dbuf since this is 2D thing and access by dbuf[i][j].
  • Deepak
    Deepak over 9 years
    Im getting following problem, How i can get JNIEnv problems please help me : env->GetDirectBufferAddress(env, buffer);