Creating a Link Description for Xamarin.Android and ServiceStack.Text

Introduction

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.

Tools

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:

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 <filename unknown>:0 
I/MonoDroid( 6650):   at System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) [0x00000] in <filename unknown>:0 
I/MonoDroid( 6650):   at ServiceStack.Text.PlatformExtensions.MakeDelegate (System.Reflection.MethodInfo mi, System.Type delegateType, Boolean throwOnBindFailure) [0x00000] in <filename unknown>:0 
I/MonoDroid( 6650):   at ServiceStack.Text.Common.WriteListsOfElements`1[ServiceStack.Text.Json.JsonTypeSerializer].GetListWriteFn (System.Type elementType) [0x00000] in <filename unknown>:0 
I/MonoDroid( 6650):   at ServiceStack.Text.Common.WriteLists`2[System.Collections.Generic.List`1[MyAssembly.Group],ServiceStack.Text.Json.JsonTypeSerializer].GetWriteFn () [0x00000] in <filename unknown>:0 
I/MonoDroid( 6650):   at ServiceStack.Text.Common.WriteLists`2[System.Collections.Generic.List`1[MyAssembly.Group],ServiceStack.Text.Json.JsonTypeSerializer]..cctor () [0x00000] in <filename unknown>: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 <filename unknown>:0 
I/MonoDroid( 6650):   at ServiceStack.Text.Common.JsWriter`1[ServiceStack.Text.Json.JsonTypeSerializer].GetWriteFn[List`1] () [0x00000] in <filename unknown>:0 
I/MonoDroid( 6650):   at ServiceStack.Text.Json.JsonWriter`1[System.Collections.Generic.List`1[MyAssembly.Group]]..cctor () [0x00000] in <filename unknown>:0 
I/MonoDroid( 6650):   --- End of inner exception stack trace ---

Basically, it’s as simple as this:

Once I was done, this is what my link description XML looked like.

<?xml version="1.0" encoding="utf-8" ?>
<linker>
  <assembly fullname="ServiceStack.Text">
    <type fullname="ServiceStack.Text.Json.JsonReader`1" >
      <method name="GetParseFn" />
    </type>
    <type fullname="ServiceStack.Text.Common.DeserializeListWithElements`2" >
      <method name="ParseGenericList" />
    </type>
    <type fullname="ServiceStack.Text.Common.WriteListsOfElements`2" >
      <method name="WriteList" />
      <method name="WriteArray" />
    </type>
  </assembly>
</linker>

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.

public string GetUpdateMessage()
{
    return JsonSerializer.SerializeToString(new { _id = Id, name = Name, description = Description });
}

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.

Wrapping Up

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 its great performance, and of course solving a problem comes with its own buzz.