I have a lib with a class that has a field with byte[].
This field becomes IList</byte/>.
However I need this field to be used as input for methods in the library which have byte[] parameters since those did not get transformed into IList</byte/>.
I cannot do a to.Array Operation since it is too slow as I am handling images.
How can I force the field to be a byte[] ?
I have tried a ton of different things but nothing seems to work.
The fact that in some places I get a list and in others an array is killing me and I am wasting too much time which I currently do not have.
In the metadata I tried many combinations but they do not work.
Example:
attr path="(..)"/field[@name='buffer']" name="managedType">byte[] attr
I was able to work around this problem but the solution is probably on the side of the creator of the lib.
Instead of having the variable public, he should have a getter for it.
byte[]
Java fields are wrapped as IList<byte>
on the C# side. The conversion with ToArray()/CopyTo() is really, really slow, because the copying process is [insanely inefficient] github.com/xamarin/xamarin-android/blob/master/src/Mono.Android/Android.Runtime/JNIEnv.cs#L1291 . Basically, what the JNIEnv.CopyArray
method does is create a byte[1]
array for every element in the source array, copy each element into its respective byte[1] array using _GetByteArrayRegion
, then copy the single element of each byte[1] array at the corresponding index in the target array. I kid you not.
The efficient solution is to use _GetByteArrayRegion to copy the entire java array into the managed byte[] array once. We can't use the _GetByteArrayRegion method directly, because it's internal, but we can copy its code:
public static unsafe byte[] ToByteArray(IList<byte> javaArray) { IList<byte> array = javaArray; var arrayHandle = ((IJavaObject)array).Handle; byte[] copy = new byte[array.Count]; fixed (byte* p = copy) JniEnvironment.Arrays.GetByteArrayRegion(new JniObjectReference(arrayHandle), 0, array.Count, (sbyte*)p); return copy; }
This method is basically instantaneous. Note that the above method is necessarily marked as unsafe, so you'll need to switch the "Allow unsafe code" option in your project's properties. This version requires the Java.Interop library. Alternatively, you can copy the [other implementation] github.com/xamarin/xamarin-android/blob/master/src/Mono.Android/Android.Runtime/JNIEnv.cs#L1147 that doesn't depend on Java.Interop.
Welcome to Xamarin World, where common sense makes no sense.
Answers
I was able to work around this problem but the solution is probably on the side of the creator of the lib.
Instead of having the variable public, he should have a getter for it.
I think we might be implementing the same library! I've hit exactly the same issue - could you let me know how you worked around it?
(...and I agree a getter would be much better!)
byte[]
Java fields are wrapped asIList<byte>
on the C# side. The conversion with ToArray()/CopyTo() is really, really slow, because the copying process is [insanely inefficient] github.com/xamarin/xamarin-android/blob/master/src/Mono.Android/Android.Runtime/JNIEnv.cs#L1291 . Basically, what theJNIEnv.CopyArray
method does is create abyte[1]
array for every element in the source array, copy each element into its respective byte[1] array using_GetByteArrayRegion
, then copy the single element of each byte[1] array at the corresponding index in the target array. I kid you not.The efficient solution is to use _GetByteArrayRegion to copy the entire java array into the managed byte[] array once. We can't use the _GetByteArrayRegion method directly, because it's internal, but we can copy its code:
This method is basically instantaneous. Note that the above method is necessarily marked as unsafe, so you'll need to switch the "Allow unsafe code" option in your project's properties. This version requires the Java.Interop library. Alternatively, you can copy the [other implementation] github.com/xamarin/xamarin-android/blob/master/src/Mono.Android/Android.Runtime/JNIEnv.cs#L1147 that doesn't depend on Java.Interop.
Welcome to Xamarin World, where common sense makes no sense.
I posted a detailed workaround with an explanation, but the entire post got mysteriously deleted.
Here's just the working function:
I still see the detailed explanation and marked it.
Thank you for your time and the detailed explanation.