ActiveAndroid TypeSerializers
Scott Carson | 11/18/2016
In the last post we learned the basics of saving and querying with ActiveAndroid. We built a simple UI for our app and glimpsed our first tangible example of ActiveAndroid at work. This post will focus on an important part of developing with ActiveAndroid: TypeSerializers.
ActiveAndroid knows how to store (serialize) primitives and your custom Model classes. It does not, and can not, know how to serialize/deserialize every Object used in Java. Some examples of commonly used types that ActiveAndroid does not know how to store are Lists, Maps, and Dates. Many classes will want to take advantage of these classes and data structures, so ActiveAndroid defined a TypeSerializer abstract class that can be extended to handle the serialization and deserialization of these data types.
TypeSerializers are not always pretty, and a bit of thought has to go into how to handle a generic type (such a List<?>
). For our car building app, the Car class has a List<Wheel>
data member that ActiveAndroid can not handle. In Part 4 of this series we simply ignored trying to display our wheels because we knew they were not going to be saved correctly and therefor not recalled correctly. The focus now will be to write a custom TypeSerializer that can not just handle our List<Wheel>
but could be easily modified to handle any List<?>
in the future.
Setting Up
For our List TypeSerializer we are going to utilize more features of the gson library to make our lives easier. This will require a few modifications to our CarBuilderApplication class:
I want to create a single Gson object that is used throughout the app. To do this I basically implement the Singleton design pattern on our CarBuilderApplication class. In the onCreate()
we first set up our global Gson instance using the GsonBuilder class. The only interesting part here is that we explicitly tell gson to ignore any variables with final
, static
, or transient
modifiers. The modifiers final
and static
are ignored because one is a constant and the other is shared between instances of a class, respectively. Although not often used, we ignore the transient
modifier because variables declared with this modifier are not serialized, thus would never be saved to our database.
Once our Gson object is built we set our static application instance to be the instance we just created. This enforces the Singleton design pattern because we know our app will only create a single application instance at any given time and we also know that our application object will be available anytime the app is running. No we can restrict access to our application object through the static getApp()
method. This allows us to safely create global instances of resources to be used throughout our app. It is valuable to note that the Singleton design pattern is not always the best solution, but for our purposes it works just fine.
Now we need to modify one line of our AndroidManifest.xml:
By changing the android:name
value under the <application>
tag we point directly to our CarBuilderApplication class instead of the ActiveAndroid application class (which our CarBuilderApplication class extends).
Implementing the TypeSerializer
Now we will take a look at how to implement our TypeSerializer class. When serializing an object to store in our database we must think about how that object will be represented in our database. Remember that SQLite supports four data types: INTEGER, TEXT, REAL, NONE. This means we need to come up with a way to represent our the Java List type as one of those four types. Not only that, but we need to create a mapping that is easy to deserialize back to the original object as well.
For many custom TypeSerializers it is easiest to convert the object to a String (or TEXT) representation. Not only that, but we know we can accurately represent objects, lists, and primitives using JSON! Our TypeSerializer will take our List
, that takes a properly formatted JSON string and a POJO class then (if successful) returns an initialized Object of the given class type.
Let’s take a look at our ListTypeSerializer extension of the TypeSerializer class:
Now I’ll break down our ListTypeSerializer piece-by-piece.
First and foremost we must extend the ActiveAndroid TypeSerializer abstract class. Doing so forces us to implement four methods. The methods getDeserializedType()
and getSerializedType()
return the type of the object we need to save and the type we will use represent it when it is saved, respectively. For us the deserialized type is Java’s List class and the serialized type is a String.
A more interesting method that we have to implement is serialize(Object data)
. This method takes in an object (presumably of type List
helper method does most of the serialization leg work. This method ensures that our Object is indeed a List, then checks that the List is of type Wheel. The code here is a little verbose, but I have not found a slicker way of doing such a check in Java. Once we know we have a List
implementation already partially formats the string as a JSON array. All we have to do is tack on a property name
and then override the Wheel classes’
(which we’ll look at soon). If we successfully build a String then we return true and print out our JSON string, otherwise we return false and log an error. Finally, we return our List formatted as a JSON string. The String should look something like this (depending on the Wheels properties):
{“wheelList”:[{“rims”:”spinner”,”tires”:”Goodyear wrangler”}, {“rims”:”spinner”,”tires”:”Goodyear wrangler”}, {“rims”:”spinner”,”tires”:”Goodyear wrangler”}, {“rims”:”spinner”,”tires”:”Goodyear wrangler”}]}Which, when formatted properly, looks just like a nice JSON object list:
{ “wheelList”:[ { “rims”:“spinner”, “tires”:“Goodyear wrangler” }, { “rims”:“spinner”, “tires”:“Goodyear wrangler” }, { “rims”:“spinner”, “tires”:“Goodyear wrangler” }, { “rims”:“spinner”, “tires”:“Goodyear wrangler” } ] }Next we have to implement the opposite; deserialize(Object data)
takes an object (this time we’re assuming it’s a String) and returns it as a List. The meat of this method is performed in the call to gson’s .fromJson(String, Class<?>)
. The POJO that gson will populate is a very simple class:
If successful, we can return gwl.wheelList
which contains the list of wheel objects we previously saved to the database.
Next I want to look at how we override the Wheel class’ toString()
method and the CarPart class’ getJsonObject()
method to give us a nice looking JSON object.
Overall, pretty easy to understand. Simply build our JSON object and then ‘stringify’ it.
Putting It All Together
Last but not least let’s change one item in our UI. Now that we know we’ll be printing out list of all the wheels, I want to wrap our TextView in a ScrollView so we can actually see it all!
Now when we run our app we should see all of our wheels that we saved to our database.
That is the basics of TypeSerializers with ActiveAndroid! They are an extremely handy and necessary tool when dealing with complex data types and object hierarchies.