Wednesday, June 23, 2021

The Simple Fix That Took Way Too Long

As Unity 2018.4 LTS is reaching EOL now I was tasked to upgrade the Unity version of the game I'm working on. The upgrade was quite easy and after a couple of days everything seemed to work. When doing the final test before we were supposed to ship the game built with this version however, I found a problem when changing profile picture. It opened the gallery app fine and allowed me to pick a photo, but as soon as I did the game crashed! 

The crashlog pointed me to the open source cropping library used by the Image And Video Picker asset we are using in this project, saying something about not finding the class. Both ClassNotFoundException and NoClassDefFoundError were shown, so I assumed that for some reason the JAR file wasn't included in the build anymore. It was also searching in some really weird paths, so I thought it could have something to do with obfuscation as well. I started going through all config files, gradle scripts and android manifests that had been changed during the upgrade, but couldn't really find anything relevant. I did a few changes and built new versions only to find the problem remaining after every try. I took to google and thought surely someone else must have had this problem, but I couldn't really find anything.

I then decided to upgrade the asset from the Unity Asset Store as I found there was a new version during my googling, but still the same error remained. At this point I finally looked closer at the error and realised it said:

Failed resolution of: Leu/janmuller/android/simplecropimage/R$layout;          eu.janmuller.android.simplecropimage.CropImage.onCreate (CropImage.java:111)

I went to the open source repo and found that line, and it was when trying to load the layout it failed:

setContentView(R.layout.cropimage);

This made me realise I'd been chasing the wrong problem. Apparently the CropImage class had been loaded, which is in the JAR, but it failed when loading the layout from resources. I unzipped an old APK and a new APK and sure enough the resources from the library was present in the old one that worked, but not in the new. 

This made me remember that old warning I've seen in Unity about putting android resources in res folders being deprecated, so I started reading up on that. Apparently what you need to do is to either put your resources in an AAR or in something called an "Android Library", that is basically an exploded AAR where you have a folder with a project.properties and it's own manifest, and a special folder structure inside that contains the assets. The CropLib however seemed to be a proper "Android Library", so I couldn't understand why it didn't work. I went googling more and still couldn't find any good resources to solve this, so I decided that once I find the solution to this problem I must publish it on my blog to make sure no one else has to spend all this time on this.

I went for lunch and an appointment with the barber, and when I came back I opened up the documentation for using Android Libraries again with restored energy, and now I noticed that Google had given me the documentation for Unity 2019.4. I changed the little drop-down in the upper left corner to 2020.3 and there I finally found the solution! In the end of the article it said:

Android Library projects must have the .androidlib extension for Unity to support them. Add this extension to your library’s root folder name (for example, mylibrary.androidlib), and place the folder in the Assets folder of your Unity Project.

This is apparently new in Unity 2020, changed by the rational that you now don't have to put it in the Plugins folder, and the asset had not been updated for this in Asset Store. So, I renamed CropLib to CropLib.androidlib, built a new version, and it worked! So in the end, I spent about two full days on something that turned out to be solved by just renaming a folder! One could have thought that the Unity upgrade should have done this, or at least pointed it out, but at least I didn't see it do that.

If anyone else have this problem and finds this blog post useful, please make a comment to make me feel better about spending all that time figuring this out.