Creating a Link Description for Xamarin.Android and ServiceStack.Text
I use ServiceStack.Text for serialisation everywhere I can, simply because it’s light, fast and versatile. It has been and might still be the fastest available for .NET, although the benchmarks are somewhat dated.
The platforms I’ve been using this on include Xamarin.Android (Monodroid/Mono for Android) for which everything had been trucking along blissfully. This was until recently when I wanted to turn on linking for all assemblies in one of my applications. Doing this reduced my application to splash-page-then-crash uselessness.
After initially thinking that parts of my own assemblies were being left out by the linker, I discovered that missing parts of ServiceStack.Text were the culprit. Some logcat reading and examination of the ServiceStack code ensued and I was able to isolate those parts and instruct the linker to keep them.
The option is there if you choose to use it, to instruct the linker to skip whole assemblies. If I was doing this on paid time I’d probably have gone down this route, but as it was I got piqued by this challenge and was determined not to bug out.
Now I stopped this exercise once my application was stable, but depending on which parts of ServiceStack.Text one uses, one might well find more of this crash causing behaviour. What follows are steps to fix this specifically for ServiceStack.Text.
The main thing we need is the ability to view our Android logs. Using logcat via the ADB shell is fine, but I love the aLogcat application. It’s great because I don’t have to be in front of my PC to capture logs specific to a crash; and of course it’s free. What I do is:
- Clear the log.
- Use my application up to the crash or logged error.
- Go back to the log and pause it.
- Email it to myself via the Share menu option.
Then I just fire it up in Sublime to navigate through it.
Finding the Gremlins
Because linking is based on static analysis the things that it’s going to leave out are methods/members not explicitly referenced in code. This can happen for base classes, for things only ever built up by IoC and/or for things accessed only by reflection.
It turns out reflection is the culprit in our case.
Fortunately all the log output for the purposes of this exercise looks similar. Here’s an example log snippet.
I/MonoDroid( 6650): UNHANDLED EXCEPTION: System.TypeInitializationException: An exception was thrown by the type initializer for ServiceStack.Text.Json.JsonWriter`1 ---> System.TypeInitializationException: An exception was thrown by the type initializer for ServiceStack.Text.Common.WriteLists`2 ---> System.ArgumentNullException: Argument cannot be null. I/MonoDroid( 6650): Parameter name: method I/MonoDroid( 6650): at System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure, Boolean allowClosed) [0x00000] in
:0 I/MonoDroid( 6650): at System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) [0x00000] in :0 I/MonoDroid( 6650): at ServiceStack.Text.PlatformExtensions.MakeDelegate (System.Reflection.MethodInfo mi, System.Type delegateType, Boolean throwOnBindFailure) [0x00000] in :0 I/MonoDroid( 6650): at ServiceStack.Text.Common.WriteListsOfElements`1[ServiceStack.Text.Json.JsonTypeSerializer].GetListWriteFn (System.Type elementType) [0x00000] in :0 I/MonoDroid( 6650): at ServiceStack.Text.Common.WriteLists`2[System.Collections.Generic.List`1[MyAssembly.Group],ServiceStack.Text.Json.JsonTypeSerializer].GetWriteFn () [0x00000] in :0 I/MonoDroid( 6650): at ServiceStack.Text.Common.WriteLists`2[System.Collections.Generic.List`1[MyAssembly.Group],ServiceStack.Text.Json.JsonTypeSerializer]..cctor () [0x00000] in :0 I/MonoDroid( 6650): --- End of inner exception stack trace --- I/MonoDroid( 6650): at ServiceStack.Text.Common.JsWriter`1[ServiceStack.Text.Json.JsonTypeSerializer].GetCoreWriteFn[List`1] () [0x00000] in :0 I/MonoDroid( 6650): at ServiceStack.Text.Common.JsWriter`1[ServiceStack.Text.Json.JsonTypeSerializer].GetWriteFn[List`1] () [0x00000] in :0 I/MonoDroid( 6650): at ServiceStack.Text.Json.JsonWriter`1[System.Collections.Generic.List`1[MyAssembly.Group]]..cctor () [0x00000] in :0 I/MonoDroid( 6650): --- End of inner exception stack trace ---
Basically, it’s as simple as this:
- Find the line immediately before ServiceStack.Text.PlatformExtensions.MakeDelegate.
- Look at the source for the method in that line.
- See what method it’s accessing via reflection there.
- Add an entry to preserve this method in your link description.
- Rinse and repeat until your issues are gone.
Once I was done, this is what my link description XML looked like.
One Last Gotcha
In a couple of places in my application, I constructed DTOs on the fly by creating anonymous types from subsets of my model fields. These were then sent immediately for serialisation to JSON. Something like this for instance.
This was returning JSON for an empty object. I chased this one all the way to System.Type in mscorlib, but I should have seen the issue immediately - there is no getter access for the properties of the anonymous type; so the linker doesn’t pick them up, so the serialiser thinks they aren’t there.
The solution was to create strongly typed DTOs for these cases and add link descriptions as required.
The result was quite satisfying, and not just because my APK is around 40% smaller than when linking SDK assemblies only. I delved into the heart of ServiceStack.Text and saw how it caches parse and write functions to achieve it’s great performance and of course solving a problem comes with its own buzz.