Reducing app bundle size

26 September 2022| Tags: android, code, debugging, xamarin
A man fixing a car

Introduction

In my Play Store Organic Traffic post I commented that I need to work on ASO (App Store Optimisation) for my app Seed:Loops . Since then my conversion rate has dropped further to a shade under 15%. The app file size is a potential reason for a low conversion rate as explained in the Google Play Console.

App size can affect your app’s install and uninstall metrics, so it’s important to monitor and optimise your app’s download and on-device sizes

My app bundle (.aab file) size before making any changes was 64.4MB.

Compressing Assets

The log hanging fruit was the sound font (.sf2 file). I use library called Bass which is a great audio library. I had already used its sf2pack tool to compress the sound font with a lossless algorithm but there was scope to more aggressively compress it with a lossy algorithm. Having tested both it doesn’t negatively impact the sound so I opted to keep the smaller file.

This reduced the sound font file size by a further 10MB.

Xamarin Linker

The Xamarin Linker Docs details how to enable linking and some of the pitfalls. By enabling it my app I fell directly into them. After deploying a release build and starting the app it immediately crashed.

Debugging Linker Errors

I found the easiest way to debug this was to run the following from a PowerShell terminal.

adb logcat | Select-String seedloops

By piping the output into Select-String and filtering to my app name it removes a lot of noise.

The word “Exception” caught my eye so used ctrl-c to break out of it and executed

adb logcat | Select-String Exception

This told me what had been removed by the linker

09-22 23:01:56.275 3295 3295 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.android.gms.ads.MobileAdsInitProvider" ...

A ClassNotFoundException for com.google.android.gms.ads.MobileAdsInitProvider.

The problem is that doesn’t translate to the assembly name that I added via nuget for Mobile Ads. To work this out I used one of my favourite tools ILSpy . ILSpy decompiles .net assemblies so it can be used to inspect classes within a dll. By dragging/dropping all the dlls from the obj\Release\120\androidx\cecil folder into it I could search for MobileAdsInitProvider to work out Xamarin.GooglePlayServices.Ads.Lite assembly contained it.

ILSpy Search (Assemblies list trimmed in the above screenshot as it was right at the bottom!)

This needed adding to Skip Linking Assemblies section in the project properties of the Android project.

ILSpy Search

I had to iterate on this process, working through another ClassNotFoundException….

09-22 23:36:17.480 9563 9563 E AndroidRuntime: java.lang.RuntimeException: Unable to get provider androidx.startup.InitializationProvider: java.lang.ClassNotFoundException: ...

And ultimately my own class constructors being linked out with the System.MissingMethodException: Default constructor not found error referenced in the docs. This was due to my use of the service locator pattern meaning there are no direct references to them in code.

The final string in my skip linking assemblies section was.

Xamarin.GooglePlayServices.Ads.Lite;Xamarin.AndroidX.Startup.StartupRuntime;SeedLoops

ProGuard

Skipping asseblies did not fix all of my errors. There was one ClassNotFoundException which wasn’t (obviously) found by searching the dependencies.

09-23 00:08:20.568 13773 13773 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: androidx.appcompat.widget.FitWindowsLinearLayout

To overcome this I used a ProGuardConfiguration.cfg file as detailed in the Xamarin Docs. I added the file to my Android project, set the build action in the properties tool box to ProguardConfiguration, and added the following line to it.

-keep public class androidx.appcompat.widget.FitWindowsLinearLayout { *; }

This works with the default code shrinker r8 selected (I did not have to change it to ProGuard).

Now the app starts and all features works as excepted.

Final Bundle Size

The aab file is now 40.75MB which is over a one third reduction in the compiled artefact.

The App Size section in the Google Play Console shows the download size broken down by assets, code etc. The charts for before and after show it’s nearer 50% in terms of actual download size!

Comparison Chart

Conclusion

I’m yet to see whether this has had a measurable impact on conversions but I’m hopeful it will. I still have more ASO to do. Next on the list is a concise video detailing its features.