Java Question Passing array back to Java in argument

divinglog

Member
Licensed User
Longtime User
Hello

I raise an event from Java with a byte array as argument:

B4X:
    private int Read(long userdata, byte[] data, int size, Object actual) {
        ba.raiseEvent(this, eventName + "_read", userdata, data, size, actual);
        ba.Log("First Byte: " + data[0]);
        return 0;
    }

In B4A I want to update the data argument which should be returned to Java. Because I do not know the size of the array before raising the event, the array is not initialized. How can I update the array in B4A without loosing the reference?

I tried this:

B4X:
Private Sub LibDC_Read(Userdata As Long, Data() As Byte, Size As Int, Actual As Object) As Int
    Dim TempArr() As Byte
   
    'Does not work:
    TempArr = File.ReadBytes(Common.LogDir, "File.bin")
    Data = Temparr

    'Does not work:
    Dim bc As ByteConverter
    Dim Data(TempArr.Length) As Byte
    bc.ArrayCopy(TempArr, 0, Data, 0, TempArr.Length)
   
    Return 0
End Sub

ArrayCopy works only if the array had already the correct size before raising the event and I do not have to redim it. But as I do not know the size before, I can't do that. So how can I pass back a byte array with unknown size in an event argument?

Thank you!
 

OliverA

Expert
Licensed User
Longtime User
raiseEvent is more equivalent to CallSubDelayed then CallSub. Therefore, the execution is

ba.raiseEvent
ba.Log
return 0

Then
LibDC_Read

Therefore, LibDC_Read does not communicate back/has any ties back to the Read method in your java source (as it stands).
 

divinglog

Member
Licensed User
Longtime User
As far as I understand it has, because arrays are passed by reference, so it actually communicates back to Java. I've tested it and it works when the array has already the correct size.

But I think I've found already a workround. The "Size" parameter contains the number of bytes which are requested. So I simply initialize the array with this size and return either the exact number of bytes if available. If less bytes are available, I return the actual number of bytes in the "Actual" argument and deal with that afterwards.
 

OliverA

Expert
Licensed User
Longtime User
But as is, you are relying on some weird timing for Java to get the data back. How does Java know that LibDC_Read has been called yet, that it has finished processing? raiseEvent raises an event in the event queue. Currently you have now mechanism in place (at least that you are showing) that ensures the data received in your Java code has been properly processed by the LibDC_Read method.
 

divinglog

Member
Licensed User
Longtime User
If I understand this correctly, this works all synchronously, so when ba.raiseEvent returns in Java code, the B4A event code has been processed. The Java "Read" function itself is a callback from JNI via CallIntMethod, so the data in the array from B4A is processed in native C code after the JNI CallIntMethod has returned.

So it's like this:

C-Code: CallIntMethod...
Java Code: Read...
B4A Code LibDC_Read...
Java Code
C-Code
 

OliverA

Expert
Licensed User
Longtime User
If I understand this correctly, this works all synchronously
Looks like you are correct (and I'm wrong). Btw, it also looks like you can have a return value for ba.raiseEvent, thus you could create a new byteArray and return it to your calling Java method and it could then pass it on as required.

Links:
https://www.b4x.com/android/forum/threads/raiseevent-or-raiseeventfromdifferentthread.83574/#content
https://www.b4x.com/android/forum/threads/handle-return-value-with-raisevent.30953/#post-285620
 

divinglog

Member
Licensed User
Longtime User
Looks like you are correct (and I'm wrong). Btw, it also looks like you can have a return value for ba.raiseEvent, thus you could create a new byteArray and return it to your calling Java method and it could then pass it on as required.

Yes, I'm already using the return value for a status code. The native library requires actually 3 values to be returned: an int as status code (0 = success) as return value, and the "Data" and "Actual" parameters which are native pointers and are updated in B4A by reference.

That's the reason why I cannot create a new array in B4A, because this is loosing the reference to the native pointer from C.

This works all perfectly fine and the data gets through to the C-code. The only problem was the initialization of the array, but the solution I've posted above works just fine. The only downside is that the array might be larger than necessary. Thank you for your help and thoughts!
 
Last edited:
Top