Android Object Serialization with Xamarin

Very often with Android you want to pass objects between activities. On iOS this is extremely easy as you are given a reference to the incoming ViewController and can easily pass the reference. On Android because of the Intent system this process becomes more complicated because many things within and without of your application can handle the intent. Thus, Android allows you to send Extra data with an intent.

We can leverage this same functionality within Xamarin rather easily. First, understand that all of your managed objects (that is C# objects) inherit from object. When you start to utilize native Android processes you need objects which inherit from Java.Lang.Object.  In doing this, you will find the need to implement a special constructor MyClass(IntPtr ptr, JniHandleOwnership jni) which must invoke the base constructor with the same signature. This constructor is commonly called when the object is being created by Android, as in the case of deserialization.

What this means is that you will need to implement wrapper classes for your PCL objects to handle the serialization.  To ease this process, I created a base class shown below.

 <pre>
namespace MarvelInfo.Droid.Models
{
    abstract class JavaSerializableObjectBase : Java.Lang.Object, ISerializable
    {
        protected JavaSerializableObjectBase()
        {
        }

        protected JavaSerializableObjectBase(IntPtr ptr, JniHandleOwnership jni) : base(ptr, jni)
        {
        }

        [Export("writeObject", Throws = new[] { typeof(IOException), typeof(ClassNotFoundException) })]
        void WriteObject(ObjectOutputStream stream)
        {
            Write(stream);
        }

        [Export("readObject", Throws = new[] { typeof(IOException), typeof(ClassNotFoundException) })]
        void ReadObject(ObjectInputStream stream)
        {
            Read(stream);
        }

        // asbtract methods
        protected abstract void Write(ObjectOutputStream stream);
        protected abstract void Read(ObjectInputStream stream);
    }
}
</pre>

Right off the bat, you can see we are using the ExportAttribute to mark the two methods in here in accordance with the serialization methods for Android Serialization(http://developer.android.com/reference/java/io/Serializable.html). These methods get called as we read from and write to the serialization engine. I have created this abstract class to simplify the implementation in the lower methods.

The most important thing to keep in mind with this process is the order. The way you write must match the way you read otherwise you the process will not work.  Here is an example of the JavaCharacter class which wraps my C# Character class.

<pre>
namespace MarvelInfo.Droid.Models
{
    internal class JavaCharacter : JavaSerializableObjectBase
    {
        private Common.Models.Character _character;

        public JavaCharacter(IntPtr ptr, JniHandleOwnership jni) : base(ptr, jni)
        {
            _character = new Common.Models.Character();
        }

        public JavaCharacter(Common.Models.Character character)
        {
            _character = character;
        }

        protected override void Write(ObjectOutputStream stream)
        {
            stream.WriteUTF(_character.Name);
            stream.WriteInt(_character.Id);
            stream.WriteUTF(_character.Description);
            stream.WriteObject(new JavaThumbnail(_character.Thumbnail));
            stream.WriteObject(new JavaComicCollection(_character.ComicCollection));
            stream.WriteObject(new JavaStoriesCollection(_character.StoriesCollection));

            stream.WriteInt(_character.Urls.Count);
            _character.Urls.Map(x =&gt; stream.WriteObject(new JavaUrlInfo(x)));
        }
        protected override void Read(ObjectInputStream stream)
        {
            _character.Name = stream.ReadUTF();
            _character.Id = stream.ReadInt();
            _character.Description = stream.ReadUTF();
            _character.Thumbnail = ((JavaThumbnail)stream.ReadObject()).GetThumbnail();
            _character.ComicCollection = ((JavaComicCollection)stream.ReadObject()).GetComicCollection();
            _character.StoriesCollection = ((JavaStoriesCollection)stream.ReadObject()).GetStoriesCollection();

            var urlCount = stream.ReadInt();
            _character.Urls = new List();
            for (int i = 0; i &lt; urlCount; i++)
                _character.Urls.Add(((JavaUrlInfo)stream.ReadObject()).GetUrlInfo());
        }

        public Common.Models.Character GetCharacter() { return _character; }
    }
}
</pre>

Each write has a corresponding read. The most interesting thing to note here is the writing of an int for the count of the list we write. As I said, we must have corresponding reads for writes so we serialize the count so we know how many reads to make.

When it comes to embedded objects, we can create new derivations of our base class and use the WriteObject and ReadObject methods which will invoke the serialization process on those objects. Below is an example of the class which reads and writes Thumbnail class which is held as a property on Character.

<pre>
namespace MarvelInfo.Droid.Models
{
    internal class JavaThumbnail : JavaSerializableObjectBase
    {
        private Thumbnail _thumbnail;

        public JavaThumbnail(Thumbnail thumbnail)
        {
            _thumbnail = thumbnail;
        }

        public JavaThumbnail(IntPtr ptr, JniHandleOwnership jni) : base(ptr, jni)
        {
            _thumbnail = new Thumbnail();
        }

        protected override void Read(ObjectInputStream stream)
        {
            _thumbnail.Path = stream.ReadUTF();
            _thumbnail.Extension = stream.ReadUTF();
        }

        protected override void Write(ObjectOutputStream stream)
        {
            stream.WriteUTF(_thumbnail.Path);
            stream.WriteUTF(_thumbnail.Extension);
        }

        public Thumbnail GetThumbnail() { return _thumbnail; }
    }
}
</pre>

It is a very straightforward and understandable process. Each method also contains a Get* method which enables me to return the C# object instance the deserialization process has built up via the readObject method.

I hope this helps you understand how to use serialization in your Xamarin.Android apps. I can understand the draw of creating a singleton and using that to hold data like this between Activities, but doing so limits your options within Android, since you wont be able to fully use the Intent system.

Advertisements

One thought on “Android Object Serialization with Xamarin

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s