Android Data Serialization Tutorial with the Kotlin Serialization Library

Learn how to use the Kotlin Serialization library in your Android app and how it differs from other data serialization libraries available out there. By Kshitij Chauhan.

Leave a rating/review
Download materials
Save for later
Share

While computers talk to each other using 1s and 0s, you often encounter APIs that communicate in a text-based format. How does an app convert human-readable text into machine-readable binary data?

The answer is data serialization and deserialization. The Android community has access to high-quality data serialization libraries such as Moshi and Gson. But, there’s a newcomer on the horizon that promises to be better than existing options: Kotlin Serialization.

In this tutorial, you’ll build an app that suggests a list of things to do when you’re bored. Through this process you’ll:

  • Learn the differences between data serialization, deserialization, encoding and decoding.
  • Work with the Kotlin Serialization library.
  • Integrate the library with Retrofit to interact with an API.
  • Write custom serializers for Kotlin classes.
  • Learn the limitations of the library.
Note: This tutorial assumes familiarity with the basics of Kotlin for Android development. To revisit the basics, consider reading Kotlin for Android: An Introduction first.

Getting Started

Download the starter project by clicking Download Materials at the top or bottom of the tutorial. Then, open the starter project, Bored No More!, in Android Studio.

Build and run. You’ll see the following screen:

First screen of the sample app

The app consists of two screens. The first screen displays a list of activities to try when you’re bored, while the second screen displays the details of a specific activity.

The starter project uses static data preprogrammed into the app. Throughout this tutorial, you’ll refactor the code to communicate with the Bored API to fetch a list of activities.

Before you start coding, take a closer look at data encoding and serialization.

Understanding Data Encoding and Serialization

An object that exists in a program’s memory consists of binary data that a computer can use directly. Serialization is the process of reducing an object to its primitive contents, like numbers, strings and arrays, in a manner that preserves its structure. Encoding a serialized object is the process of converting its primitive contents to an output format by following a specific set of rules.

Consider an object in memory that consists of two primitive values:

val hugeParty = Party(pizzas = Int.MAX_VALUE, people = Int.MAX_VALUE)

Its serialized representation contains its primitive values in a structured format. It’s not important what this representation looks like: What matters is that it contains information about the object so it can be encoded into a specific format.

"01101110 01110101 01101101 01010000 01101001 01111010 01111010 01100001 01110011 ..." // Hypothetical serialized representation

The object’s encoded representation contains its data represented according to the rules of a specific format, such as JSON, XML, YAML or TOML. Here’s an example of the JSON representation:

{ "pizzas": 2147483647, "people": 2147483647 } 

Of course, before a computer can use encoded data it must decode the data.

Understanding Data Decoding and Deserialization

Decoding is the process of parsing encoded data to produce a deserialized representation of an object consisting of its primitive contents while maintaining its structure. Deserialization is the process of converting such a decoded stream of primitives into an object. Thus, decoding and deserialization are the opposites of encoding and serialization, respectively.

The Kotlin object is broken down into primitives via serialization and encoded into data. Then, the data can be decoded into primitives and deserialized into a Kotlin object. Here’s a diagram that summarizes the flow of converting a piece of data to and from an encoded representation:

Digram showing data serialization flow

Now that you understand the differences between serialization and deserialization, it’s time to get started with the Kotlin Serialization library.

Kotlin Serialization

The Kotlin Serialization library, or kotlinx.serialization, combines a compiler plugin and a runtime API.

Note: Don’t let the name kotlinx.serialization mislead you: The library supports both serialization and deserialization. Naming things is hard, and long names aren’t memorable. Therefore, it’s common to refer to such libraries as data serialization libraries even though they also support deserialization.

Head over to Android Studio to add the following dependencies to your project.

First, open the project level build.gradle and add the Kotlin Serialization plugin to your classpath in the dependencies block:

dependencies {
  // Other classpath declarations
  classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}

Then open the app module’s build.gradle and apply the plugin in the plugins block at the top of the file:

// Other plugins
apply plugin: "kotlinx-serialization"

Next, add a dependency on the JSON encoder module in the dependencies block:

dependencies {
  // Other dependencies
  implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
}

Finally, sync the project to download these dependencies.

But wait, why is the Kotlin Serialization library split into a compiler plugin and an encoder module? To understand why you need to learn more about how the library works.

The Compiler Plugin

Traditional data serialization libraries use one of two approaches:

  1. Code Generation, such as Moshi.
  2. Reflection, like Gson.

While the code generation approach yields much faster runtime performance, it also leads to increased build times. On the other hand, reflection-based libraries offer slow runtime performance but impose little to no penalty on build times.

Like Moshi, the Kotlin Serialization library relies on code generation. However, unlike Moshi, it uses a compiler plugin to generate code instead of an annotation processor. Using the compiler plugin, the Kotlin Serialization library can offer excellent runtime performance while maintaining fast build times.

The JSON Encoder Module

In the previous section, you learned about the differences between serialization/deserialization and encoding/decoding. While the compiler plugin provides the former functionality, the library delegates the responsibility of encoding/decoding data into specific formats to separate modules.

The JSON Encoder module lets you convert serialized Kotlin objects into their JSON representation and vice versa. The library also offers official modules for other formats, like CBOR and Protocol Buffer. You can even use third-party modules for other formats.

Comparing Kotlin Serialization Library to Moshi and Gson

The Kotlin Serialization library offers quite a few advantages over the existing data serialization libraries Moshi and Gson, such as:

  • Faster runtime performance than Gson and Moshi in reflective mode through code generation.
  • Faster build times than Moshi through the use of a compiler plugin rather than an annotation processor.
  • Better support for Kotlin’s type system compared to Gson, including support for nullable and default values.
  • Support for many encoding formats through the use of separate library modules.

Now that you understand the advantages of Kotlin Serialization, it’s time to learn how to use it!