Reducing the App size in Xamarin. (Deep Dive)

Let’s find out!Kindly, read this awesome blog by Adam Pedley on Reducing app size in Xamarin Forms where he has added a lot of important pointers and that is what compelled me to share my experience.

(I have also picked up some of the content from his blog)There are a few things you can do to make your app Lighter when developing with Xamarin.

Linking :Xamarin applications use a linker to reduce the size of the application.

The linker employes static analysis of your application to determine which assemblies are actually used, which types are actually used, and which members are actually used.

The linker then behaves like a garbage collector, continually looking for the assemblies, types, and members that are referenced until the entire closure of referenced assemblies, types, and members is found.

Then everything outside of this closure is discarded.

If you check the Official documentation for Linking on Android & Linking on iOS it shows you how do you perform the linking operation.

The primary mechanism for controlling the linker is the Linker Behavior (Linking in Visual Studio) drop-down within the Project Options dialog box.

There are three options:Don’t Link (None in Visual Studio)Link SDK Assemblies (SDK Assemblies Only)Link All Assemblies (SDK and User Assemblies)The Don’t Link option turns off the linker; This is useful for troubleshooting runtime failures, to see if the linker is responsible.

This setting is not usually recommended for production builds.

The Link SDK Assemblies option only links assemblies that come with Xamarin.

All other assemblies (such as your code) are not linked.

And hence any of your third-party libraries will not be affected.

This is the easiest and safest type of linking and must be your default.

As the default packages that you’ll need to get started with Xamarin Forms on iOS and Android all have linking support, you won’t have a problem.

Now you might have noticed that a lot of times when you try to use this some packages start to throw random errors, sometimes about not finding the constructor of certain classes.

You will notice that your project crashes regularly saying that it can’t find the ctor() for a specific class from a libraries namespace.

This is because linking has removed that classes constructor that your code is looking for.

There are several libraries out there that aren’t built with linking support.

It’s likely that these libraries must be using Reflection as part of their internal working and so the compiler won’t think some of their methods and properties are in use and hence will decide to remove them.

There are a few ways to work around this problem.

I prefer compiler link skip, which skips linking for a certain library.

This has to be done in each platform that you are using the dll.

How to skip AssembliesiOSGo to your iOS project's properties and under iOS build tab do the following In the “Additional mtouch arguments:” field, you need to add the names of the frameworks dll’s that you want to ignore alongside a –linkskip=LibraryName .

See to it that you do not add the .

dll extension and separate them by a space.

AndroidGo to your Android project’s properties and under Android build tab do the following In the “Ignore assemblies” field, type the name of the assemblies you want to ignore, separated by ; and without the .

dll extension.

The Link All Assemblies option links all assemblies, which means your code may also be removed if there are no static references, as it will investigate everything and bring down your app size.

When set to this type the linking process is quite aggressive and the chances are that the app will remove something that you actually need.

If you manage to get all your library frameworks linking successfully then you’re 80% of the way there.

For a Full linking, you need to link your own code as well.

Go back into the project settings and select “Link all”This setting is pretty much the same except it opens up your own code to be linked.

In order to prevent your classes from being linked, you can use them [Preserve] attribute form the Xamarin.

Internals namespace.

This will tell the compiler to ignore a method, property or class.

Firstly, any properties on your Binding Context that you’re only referencing from XAML and never in the code behind, you will need to add a Preserve attribute to them.

Another thing to watch out for is your Dependency Services.

These will be linked automatically as there is no direct reference to the code from the PCL or the native project DependencyService.

Get<IPlatformSpecificOperations>()doesn’t actually reference that class, Finally, custom renderers just like dependency services aren’t directly referenced in the codebase so you will have to do the same for them as well.

Now, James Montemagno has done extensive experimentation on this and in his blog here you can see how he has shown his bike apps example:So employing this practice with my Bike Now app, which uses Json.

NET, Android Support v4, v7, Google Play Services, and Xamarin.

Insights, we can compare and contrast the app size when we build our app to support all three ABIs (we will talk about this next!).

– Don’t Link: 40.

7MB- Link SDK Assemblies Only: 18.

7MB- Link All Assemblies: 13MBAs you can see linking correctly can make a huge impact, but we can do even better!I wouldn’t suggest you turn linking on during development as it has a substantial impact on build time, Only do it when you plan on releasing your application and always have buffer time in your bucket as this usually causes issues and takes a little time to understand what is causing it in the first place.

And always do a 100% test after you release with these settings.

Removing unwanted packages:NuGet packages or .

dll’s, in general, are known to substantially increase the build size.

Sometimes we are very neglectful about this(at least I am guilty here) and then at one point in your project you are confused whether or not a particular package is even in use or not.

Recently I saw a Question on StackOverflow, where the questioner had the following already done:Proguard enabledConfiguration: ReleasePlatform: Active (Any CPU).

Enable Multi-Dex: trueEnable developer instrumentation (debugging and profiling): falseLinking: SDK and User Assemblies(Tried SDK assemblies only also)Supported architectures: Selected allSo the only possible thing that came in my mind at that point was that the person was neglecting the unused packages and in the end removing the unwanted images and assemblies is what solved his issue.

Doing this brought down his APK size from 80Mb’s to 21Mb’s that is almost a 300% decrease in bundle size.

If you are wondering how to find unused packages in Visual Studio IDE you can check this StackOverflow question.

Image Compression:If you have a lot of high-quality images in your app, you can easily compress them, without losing any quality.

Go to any image compression website(i use TinyPng as everyone suggests it), to compress PNG or JPEG files.

I have seen a drastic image file size reduction where sometimes the image size falls down to half or more, with no quality loss.

It won’t help with memory usage but could cut off MB’s on your app package file size.

Using SVG instead, is, of course, another option, if you are just starting to build your app but just do you know SVG’s are not always the good option like for eg when your image is are very vivid and has tiny and important details in it.

App Thinning in iOSIf you enable Bitcode, this option is on by default, you enable iOS to apply it’s app thinning when you submit it to the App Store.

As such, only the relevant code for each architecture will be sent to the user's device, when they download your app.

AOT in AndroidIf you use AOT in your app, to speed up the start times and performance of your app, you will notice that the file size increases quite a bit.

Android AOT Additional ArgumentsThanks to Adam Pedley post that I have linked above, I actually found these interesting arguments you can pass to the AOT compiler, to reduce your binary size further.

While I have not seen what happens to a stack trace, in any crash report, I can at least confirm, they do reduce your file size.

As a precaution, I would ensure your analytics and logging is well written, to capture as much data as possible, to not rely on the stack trace.

As a quick reference, you can add both of these to your Android csproj with the following.

<AndroidAotAdditionalArguments>no-write-symbols,nodebug</AndroidAotAdditionalArguments>no-write-symbolsIn the resulting AOT, normally full method names are included in the resulting *.

so files.

You can stop this by passing this attribute.

nodebugDebug information may also be coming out in the final AOT binary, hence this option turns it off.

ProGuardProGuard is normally known as a code obfuscator, meaning that it will try to change the method names and code structure if possible, to make it very hard for people to reverse engineer your code.

I personally don’t believe in obfuscation, as it only slows them down, it never stops them.

But in addition to this, ProGuard also optimizes and minimizes your code, resulting in additional file size reductions.

I am always a sucker for a reduced file size you see :DAPK splitting:On Android, there are ABIs (Application Binary Interfaces) that you can support when you release your application.

The most used will be the armeabi-v7a, however, there are still tons of devices that support and run the old armeabi ABI and even x86 devices as well.

But in my research, most of the x86 supporting devices are either obsolete or emulators which I to be very honest do not care about supporting, But to ensure your app is reaching the most users you most likely have to go into the project settings and select every single ABI.

However, for every ABI that you select you are actually bundling a separate libmonodroid and sgen with your app.

Don’t believe me then rename your .

apk to .

zip and take a look in the lib folder:This, of course, makes sense as you would need a different version of monodroid and sgen that supports that ABI.

The issue is that you now have all of these libraries bundle into a single APK and your users will be downloading all of them!.The solution for any Android developer (even Java devs) is to simply split up your APKs and upload all of them to Google Play!Notes:You may need to restart VS after selecting the checkbox and ensure this flag is set in your csproj:<AndroidCreatePackagePerAbi>true</AndroidCreatePackagePerAbi>Additionally, your new APKs will be in your /bin/Release folder and will be marked with Signed in their file name.

Understanding the Constituent Pieces of APKs in Xamarin.

AndroidSummary:Xamarin Apps can be heavy and the above methods can help you reduce the Application size to the minimum but always remember you will not able to make your app as the same size as a native app because, Xamarin.

Forms and Mono, add quite a significant amount of overhead to the file size.

AOT increases that even further but if you perform everything mentioned above correctly you will be able to come up with an acceptable file size for your IPA/APK.

In case you think I have missed something go ahead and add it in the comments I will make sure I add them in the post, Also if you find something incorrect in the blog please go ahead and correct me in the comments.

You can reach out to me on LinkedIn or StackOverflow!.I am always available! :PThank you so much for reading the post!.

. More details

Leave a Reply