Android Developers
Published in

Android Developers

DataStore and Kotlin serialization

In the following posts from our Jetpack DataStore series, we will cover several additional concepts to understand how DataStore interacts with other APIs, so that you’d have everything at your disposal to use it in a production environment. In this post, we will be focusing on Kotlin serialization. We will be referring to the Preferences and Proto codelabs throughout this post, for code samples.

Data Store with Kotlin Serialization

In our previous posts, we’ve covered how Preferences and Proto DataStore approach structuring and serializing your persisted data: Proto uses typed objects backed by Protocol Buffers, while Preferences uses key-value pairs as our data representation, similarly to SharedPreferences. Under the hood, both implementations save data on disk, in a file, using protocol buffers. But, DataStore also allows you to customize this and use data classes and Kotlin serialization, giving you the type safety advantage of Proto DataStore, but without having to use protobufs. Let’s see how serialization works by default for each of these approaches:

Serialization options with DataStore

Preferences DataStore simplifies working with protobufs by adding an additional layer on top of its low level Proto implementation. This way, you get a lot of the benefits of working with DataStore, but using a SharedPreferences-like way of structuring data, using key-value pairs.

If we look at the Preferences API’s PreferencesSerializer and our custom ProtoUserPreferencesSerializer, you will notice that they pretty much do the same thing. PreferencesSerializer just has an additional step of transforming the key-value pairs into protobufs and vice versa:

Implementing DataStore with Kotlin serialization

If you’d like to use Kotlin serialization to structure your data, all you need to do is define a fully immutable data class and implement a DataStore Serializer.

DataStore relies on equals and hashCode which are automatically generated for data classes. Data classes also generate toString and copy functions which are useful for debugging and updating data:

🚨 It is very important to ensure that your class is immutable since DataStore is not compatible with mutable types. Using mutable types with DataStore will result in bugs due to data inconsistency and race conditions. Data classes aren’t necessarily immutable by default, so make sure you use vals everywhere instead of vars:

Arrays are mutable, so you shouldn’t expose them. Even if we use the read-only List as a member of our data class, it’s still mutable. Instead you should consider using immutable/persistent collections:

Using mutable types as a member of your data class makes it mutable. Instead, you should ensure that all members are immutable types.

Kotlin Serialization supports multiple formats, including JSON and Protocol Buffers. In this example, we’ll continue with JSON.

In order to read and write your data class to JSON using Kotlin serialization, you need to annotate your data class with @Serializable and override the Serializer’s writeTo() and readFrom(). Here’s an example with UserPreferences:

⚠️ Parcelables are not safe to use with DataStore because the data format may change between Android versions.

Pass the newly created UserPreferencesSerializer into DataStore when constructing it:

Reading data looks the same as with protobufs:

You can use the generated .copy() function to update data:

Using DataStore with Kotlin Serialization and data classes can reduce boilerplate and help simplify your code, however, you must be careful not to introduce bugs through mutability. All you need to do is define your data class and implement the serializer.

To be continued

We’ve covered Kotlin Serialization and the necessary steps on how to use it for structuring DataStore persisted data — using fully immutable data classes, writing them to JSON with the@Serializable annotation, overriding our Serializer’s writeTo() and readFrom() and finally, passing this to our DataStore instance.

Join us for the next post of our series where we will be looking into how to do synchronous work with DataStore.

You can find all posts from our Jetpack DataStore series here:
Introduction to Jetpack DataStore
All about Preferences DataStore
All about Proto DataStore
DataStore and dependency injection
DataStore and Kotlin serialization
DataStore and synchronous work
DataStore and data migration
DataStore and testing

--

--

--

Articles on modern tools and resources to help you build experiences that people love, faster and easier, across every Android device.

Recommended from Medium

Jetpack Compose

Drawing a rounded corner background on text

How to auto just text size in Android

Kotlin Android Extension- The deprecated warrior

Intro to RxJava & It’s use in Android

A Detailed Guide for Room Database with Kotlin, Rx, and MVVM

How To Share An Image From Your Android App Without Exposing It To The Gallery

TOP 5 ANDROID PROJECTS IN 2021

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Simona Stojanovic

Simona Stojanovic

Android Developer Relations Engineer @Google

More from Medium

DataStore and dependency injection

Android Touch System — Part 2: Common Touch Event Scenarios

Barista — Enjoyable Espresso Android UI Tests

Test Coverage for Android multi-module project