How Xamarin.Android AOT Works

AOT stands for Ahead of Time compilation, and compiles your code, to the native platform, dependent upon the architecture. The reason you would use AOT, is because it has a drastically reduces startup time, and app performance. You can see some results from Improving Xamarin.Forms Startup Performance. The down side is you also get drastically larger file sizes, which you can see and even try to reduce, via Reducing App File Size In Xamarin.Forms.

This post, we will be going through what AOT does during the build process, and when your app starts.

Quick Definitions

Before we delve into it, here are a few quick definitions on terms I will be using.

Mono Runtime – This is a collection of core technologies, that’s purpose is to run ECMA CIL byte codes into native code. ECMA CIL byte codes is what .NET code is compiled into. The mono runtime allows this compiled .NET code, to run on different platforms and architectures, such as Android and iOS.

JIT – Just In Time. This refers to the JIT compiler, where code is compiled into native code, at runtime. This is part of the Mono runtime.

ABI – Application Binary Interface. This generally means what architecture you are using, e.g. x86, armeabi

ART – Android Runtime, is the default runtime for Android devices Lollipop and higher.

Dalvik – The old Android runtime, on Android devices lower than Lollipop. We won’t be focusing on this at all, since I consider it obsolete, but it explains why some things are named.

dex – This stands for Dalvik Executable, and is the byte code files, for both the ART and Dalvik runtimes.

ACW – Android Callable Wrappers are created whenever the Android Runtime needs to access the managed code.

Normal Build Process

This is a very simplified version, there are many other steps that also occur.

First the C# (or other .NET code) is compiled into DLLs, then an ACW is created, that provides the interface into the managed runtime. Mono libraries are also taken, and these are all placed into an APK.

Normal APK

An APK, is just a zip file, containing many other files. Here is what a standard APK looks like, when unzipped.

It’s fairly straight forward, the Android Manifest lets you know about what application and activity should start, and the classes.dex contains the to initialize the mono runtime and start the app. The .NET DLLs that will be JIT’d on runtime, are contained in the assemblies folder.

Load Sequence

This is the load sequence of a Xamarin.Android app.

All of the .NET Code is run via JIT, in this process.


Now, if we switch on AOT, lets look at the APK. Once we extract it, if we go into the lib folder, there is a folder for each architecture we chose. In this case just x86. If we go in this folder, we see all the static natively compiled libraries. You can see the size of these are quite large.

Now we have these .so files, we no longer need the JIT to compile these to native code, when the app starts. We did it on the build machine. This is why it’s quicker. However, the mono runtime will still run, because JIT can’t be removed completely, nor can other mono runtime features.

Full static compilation

Full static compilation is compiling everything to natively compiled code, and having no JIT (Just in time) engine, via the Mono runtime. Xamarin.iOS achieves this, but Xamarin.Android doesn’t. You can do this by running mono –aot=full. You can actually try this command when doing AOT, by passing it in the AndroidAotAdditionalArguments, but I will save you the trouble and mention it doesn’t work. Even with a Full AOT, you still need the mono runtime for other functions such as garbage collection.

Native Load sequence

Why is the native load sequence so fast? If you looked at my original article  Improving Xamarin.Forms Startup Performance, you would notice that a native Android application does need any additional AOT process, when building, and it runs faster than any Xamarin.Android application.

Android has a process on it called dex2oat. Yes oat is actually a purpose misspelling of aot, to say of ahead time. Anyway, this process is run on install of any android app, and it converts dex files to native files.

Hence all native android apps are AOT’d, but they are done so on the mobile phone, not on your build machine. This unfortunately can not be done with Xamarin.Android, because we have DLLs in CLI, not dex files. Unless we can convert our application to java byte code, in a dex file, or Microsoft reach some kind of agreement with Android to include a mono AOT process in the operating system, I don’t think there is a way around this. It’s a penalty that each Xamarin.Android app has to pay, to use mono.

Take Away

AOT drastically improves startup times, but also file sizes, and there isn’t anything that can be done, unless the Xamarin.Android team, manage to do full static compilation. As to why that can’t be done, that is currently at the limit of my knowledge, but I am going to assume it’s not easy, otherwise they would have done it already. Hopefully you picked up some new information on the processes that happen behind the scenes, when compiling a Xamarin.Android application, and why native Android applications are so much faster and smaller.


  1. Gergely Nagy

    Hi Adam,
    Thanks for this great article! Even if I understand more clearly how compiling works I have an issue with an ongoing project that is right in the subject.
    I am on VS Mac and doing a Forms project with Android and IOS platforms. I was testing the Android version before the 2.4 update and was working fine. Lately I was testing the project on iOS and now I wanted to switch to Android to check how things go and suddenly I have the following exception at startup:
    Didn’t find class “md5a7903531ac28e2eb4c8576cdf2655bda.ConnectivityChangeBroadcastReceiver” on path: DexPathList[[zip file “/data/app/ch.elopsis.grat_ici-1/base.apk”],nativeLibraryDirectories=[/data/app/ch.elopsis.grat_ici-1/lib/arm64, /data/app/ch.elopsis.grat_ici-1/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64]]

    The app output is:
    [Mono] Assembly Loader probing location: ‘/storage/emulated/0/Android/data/ch.elopsis.grat_ici/files/.__override__/Plugin.Connectivity.dll’.
    [Mono] AOT: image ‘/storage/emulated/0/Android/data/ch.elopsis.grat_ici/files/.__override__/’ not found: dlopen failed: library “/data/app/ch.elopsis.grat_ici-1/lib/arm64/” not found
    [Mono] AOT: image ‘/usr/local/lib/mono/aot-cache/arm64/’ not found: dlopen failed: library “/data/app/ch.elopsis.grat_ici-1/lib/arm64/” not found
    [Mono] Assembly Ref addref GRATICI[0x7b60e26700] -> Plugin.Connectivity[0x7b60e12200]: 3
    [Mono] Assembly Ref addref GRATICI[0x7b60e26700] -> Plugin.Connectivity.Abstractions[0x7b60e12100]: 4

    I have the latest build tools and targeting the latest available level. The output says it is AOTing but in VS mac this setting is not available so I don’t know how come it is doing that.
    So I am kinda lost at this point, I don’t know where to continue to resolve the problem.
    Oh and I also switched on MultiDex, because if I don’t do it I have a java error 2.
    I have also looked into the classes2.dex file and found the ConnectivityChangeBroadcastReceiver but its under a different package name. I don’t know how its possible.

    1. Adam Pedley

      Didn’t find class “md5a7903531ac28e2eb4c8576cdf2655bda.ConnectivityChangeBroadcastReceiver” is the issue. Do you have a ConnectivityChangeBroadcastReceiver defined in your manifest and Xamarin.Android project somewhere? Broadcast receivers are Android specific, which is why it is only appearing in that project.

      And if you want to show XML or HTML in comments here, you need to HTML encode them first, otherwise WordPress strips them out.

      1. Gergely Nagy

        I just checked and yes, it is defined in the Xam.Plugin.Connectivity plugin that I use in the project.

        1. Adam Pedley

          Does this work without AOT, or is it only occurring with AOT enabled? Because this error looks more like a possible linker issue. You could try doing Link SDK Assemblies only, instead of SDK and User assemblies. If that works, you can then reference that broadcast receiver to stop it being stripped via the linker.

            1. Adam Pedley

              The option is only via the GUI if you have the Enterprise edition. Otherwise, you need to manually edit your csproj and add in <AotAssemblies>true</AotAssemblies>

              Not sure if that was on oversight on Xamarin’s part or intentional.

              1. Gergely Nagy

                I have the community version of VS2017 so there is no gui and I never added
                This error comes on Debug config and the linker is set to Don’t Link.
                I already tried to set false explicitly in csproj but didn’t help. Let me try it again though.

  2. Gergely Nagy

    So I have just set explicitly AotAssemblies false in the csproj file, the linker is set to don’t link, debug configuration and the problem persists.
    The app output still talks about the AOT image. This is incredible.

  3. Gergely Nagy

    So basically I face two different problems. One is AOT being used while I don’t want to use it and not being able to turn it off and the other problem is the linker maybe?

    So the app crashes at:
    LoadApplication (new App ());
    in MainActivity, so what I did that I added this a reference before so the thing looks like:
    Plugin.Connectivity.ConnectivityChangeBroadcastReceiver receiver = new Plugin.Connectivity.ConnectivityChangeBroadcastReceiver();

    LoadApplication (new App ());

    And the weird thing is that the app crashes in between.. where the new line is. So somehow the binaries not getting updated?? But I always do a clean, delete obj and bin folders, delete from the device.
    I am at my wits end. Thanks anyway for having looked at this.

  4. Michal Dobrodenka

    Currently when I try to aot my app, the app crashes. At least on x86. For arm it can be compiled and run. But it has many issues. Tested 2 months ago. Our app is xamarin. Native.

    Looking forward for a time when AOT will be production ready.

    I’ve tested xamarin.forms for our project, but performance, especially startup time (7sec for demo app with simple list) was not acceptable.

  5. ZenDroid

    Hi, Adam
    Thanks for this great article. I enabled AOT (with ABI, and proguard). Now my app have time first start 5 sec.(without AOT 10 sec). But new size my app =39 Mb, old size = 17 Mb.
    I want to use linker option “Sdk and user assemblies”. Can this option help me?

  6. ZenDroid

    If I understand correctly for AOT only one way – use use linker option “Sdk and user assemblies”. Did you use this configuration?

    And another question – if I use AOT – I do not need use obfuscatin for my dll?

    1. Adam Pedley

      The linker is different than AOT. The linker just removes methods from the DLLs, that it determined it didn’t need, after it has been compiled, but before AOT.

      Also I am not sure how to obfuscate everything in Xamarin properly. As you can see by this article it also keeps a copy of the .NET DLLs in the assemblies folder for metadata. As such, someone could just use those.

      Unless you are really concerned about someone copying your app for some reason, because it does complex client side calculations, or similar, I normally don’t worry about obfuscation.

      Even if you didn’t obfuscate anything, it would still probably take longer for someone to decompile your code and setup a new project themselves and copy everything across. This is again, assuming you don’t have any complex IP in the app.

  7. ZenDroid

    When I created apk with AOT(release) then inside there is no folder assemblies , and not my dll files. Only files * I think it’s hard to reverse engineer so files.