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.
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.
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 MainActivity.java 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.
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.
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.