Unity Android Interop: Can I create a java.util.ArrayList from Unity code and use it?

I’m fiddling with a card reader SDK where I’m trying to implement it without any plugin wrapper. Just straight AndroidJavaObject and/or AndroidJNI.

This SDK has a method that returns a java.util.ArrayList of type java.lang.String that is a list of usb connected card readers. In my Unity code, I’m invoking the method to get the list like so:

var deviceList = new AndroidJavaObject("java.util.ArrayList");

try {
   long result = scard.Call<long>("SCardListReaders", unityContext, deviceList);
   Debug.Log("SCardListReaders result: " + result);
}
catch (Exception e) {
   Debug.LogError(e);
}

int deviceCount = 0;
try {
   deviceCount = deviceList.Call<int>("size");
}
catch (Exception e) {
   Debug.LogError(e);
}

Debug.Log("Device count: " + deviceCount);
Debug.Log("Connected devices:");
for (int i = 0; i < deviceCount; i++) {
   try {
      // var device = deviceList.Call<string>("get", i); // Soft crash here
      var device = deviceList.Call<AndroidJavaObject>("get", i); // Soft crash here
      Debug.Log(device);
   }
   catch (Exception e) {
      Debug.LogError(e);
   }
}

Line 24 is where a soft crash happens and the rest of the script doesn’t function. Not a single exception is caught either.

I’m womdering if something is wrong with the way the ArrayList is being made, but the API call I’m using is putting data into the ArrayList and I can see there is a element count. The issue is when I’m trying to get data. I tried several types that would seem correct.

To test the fuctionality of Java ArrayLists in Unity further, I made one and then tried to add elements to it just from Unity code. I get the same soft crash when invoking the add method like I did with the get method.

   public void ArrayListTest () {
      NanoDebug.Log("Array List Test derp");
      try {
         var arraylist = new AndroidJavaObject("java.util.ArrayList");
         arraylist.Call("add", 1); // Soft crash here
      }
      catch (Exception e) {
         NanoDebug.LogError(e);
      }
      NanoDebug.Log("Done");
   }

Note that an exception is never caught, and my “done” print statement never executes. The script also becomes unreponsive.

Inspecting how Java ArrayLists work, I realize this invokcation from Unity is never giving the ArrayList a generic type. Shouldn’t be invoked with something like this syntax?

new AndroidJavaObject<string>("java.util.ArrayList")
new AndroidJavaObject("java.util.ArrayList", typeOf(string))

???
I have no idea how this would work. I just know that making an Java ArrayList from Unity code seems to not be giving it a generic type, so wonkyness starts to happen when I use add or get methods.

So I’m asking, is it even possible to work with a Java ArrayList from the Unity side?

Nothing shown in the adb logcat either??

Unsure… I thought the Unity stuff was just sort of a reflecto-wrapper that takes objects and strings and tries to find related methods and calls them for you.

Perhaps it is necessary to additionally wrap any arguments you give to add() rather than just passing an integer in??

I’m having a difficult time using logcat because my tablet cannot stay plugged into my windows. I’m developing for a USB peripheral and that USB port on the tablet is always occupied.

My best method at debugging without a debugger attached is to capture Unity’s debug output and write it to a text field.
This of course has issues if the GUI doesn’t respond.

That’s unfortunate. I don’t think that log would contain exceptions thrown… maybe though??? Next thing is to try and find example code making one of those ArrayList objects from Unity and playing with it.

Also what about some kind of USB hub thingy? Dunno how to owner / attacher rights are sorted out in USB though…

I’ve tried a hub. The Samsung tablet I’m developing with doesn’t recognize a computer through it where it can enable USB debugging. I’m figuring this out as I go. I hope to come across a better solution.

I think I would have reached for the plugin wrapper by now. :slight_smile:

BTW, can you bang on the API you want from native JNI? It’s SUPER-easy to drop a C / C++ file into the Plugins/Android folder and its functions just gets linked in and available to your Unity code via the C# interop stuff.

Have you considered running adb via tcp/ip to eliminate the need for USB.

Unfortunately, the SDK I’m trying to use is written in Java, so Java methods are what I need to call.

I have, but I haven’t put the time into learning how to set it up yet. I was hoping for what I’m trying to investigate with this Smart Card Reader SDK written in Java would be a quick adventure, but I’m starting to get wrapped up in a bunch of different nuances of calling Java code from Unity. I may just need to set that up so I can properly debug things.

I discovered some errors in my logging functions. They were silently throwing cast errors. I resolved that and went back to investigating using a Java ArrayList purely from Unity code.

I can make an ArrayList.

var arraylist = new AndroidJavaObject("java.util.ArrayList");

I can retrieve properties of the arraylist

var size = arraylist.Call<int>("size");
Debug.Log(size); // 0
var isEmpty = arraylist.Call<bool>("isEmpty");
Debug.Log(isEmpty); // true

But when I try to call add or get I get the following AndroidJavaException:

var arraylist = new AndroidJavaObject("java.util.ArrayList");
try {
   arraylist.Call("add", 1);
}
catch (Exception e) {
   Debug.LogError(e);
}

UnityEngine.AndroidJavaException: java.lang.NoSuchMethodError: no non-static method with name='add' signature='(I)V' in class Ljava.lang.Object;

I’ve confirmed add does exist according to the Java API
https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html

Is this an Android/Java bug, or does it not recognize these methods for techincal reasons?

I’ve also noticed that ArrayList being a part of generics, there is no way to declare what type of data the ArrayList handles when you use new AndroidJavaObject("java.util.ArrayList").
So, is managing a Java ArrayList from Unity code even possible? It seems like you can only pass it around and any such management has to be done on the Java native side.

1 Like

You should use:
arraylist.Call(“add”, 1);
Cause java function “add” returns boolean value

1 Like

Great topic.

@ghostravenstorm thank you for super detailed explanation of the issue. I was facing the same issue. And @MaksymAndroid, I almost gave up on your answer which is pointing to absolutely correct solution, but the snippet you wrote is wrong. :slight_smile: It should say:

arrayList.Call<bool>("add", 1);

Thank you for pointing out to this, I completely forgot that add is returning boolean natively.

Cheers. :beer: