4

Android Jetpack Proto DataStore

 2 years ago
source link: https://proandroiddev.com/android-jetpack-proto-datastore-a11ff8edcda8
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Android Jetpack Proto DataStore

A Jetpack recommended solution for storing data using Protobufs that provides type safety

Introduction

SharedPreferences the common way used for storing key-value pairs in local storage. Datastore is a replacement to SharedPreferences to overcome its shortcomings. Datastore is an advanced data storage solution that was built using Kotlin coroutines and Flow to store data asynchronously, consistently, and transactionally. There are two ways to store data in DataStore. Those are Preferences DataStore and Proto DataStore. Please check out my previous post about introduction to Datastore and Preferences DataStore implementation

In this post let’s see we will know more about the following:
- Proto DataStore
- Protobufs
- Basic implementation of Proto DataStore

Familiarity with coroutines and Kotlin Flow is needed for understanding this.

If you want to jump directly to the code base, check out the GitHub repo.

What is Proto DataStore?

We are habituated with the system of a key-value pair for storing data in Shared preferences and Preferences DataStore.However,Proto DataStore stores the dataas instances of a custom data type rather than key-value pairs. Proto DataStore needs a pre-defined schema with data types and instances that need to be stored. This schemaapproach helps in providing the type-safety with predefined data types. We need to Define a schema using Protocol buffers.

  • Stores dataas instances of a custom data type
  • Defines the schema using Protocol buffers. Using Protobufs allows persisting strongly typed data.
  • They are faster, smaller, simpler, and less ambiguous than XML and other similar data formats.

We need to follow a new serialization mechanism for Proto DataStore which we will see later in this post. Before jumping to the implementation let’s know a little bit about Protocol buffers.

What are Protocol buffers?

Protocol buffers mostly referred to as Protobufs are language and platform-neutral mechanisms of serializing data. Protobufs can be best suited for scenarios where faster communication over the network is needed or for storing data. Google created the ProtoBuf format in 2008. It’s an alternative solution to JSON, XML to serialize and deserialize data as fast as possible.

The current version of Protobuf is proto3. We will use this proto3 version later to create our proto datastore. It is important to know about the mechanism of Protobufs. In this mechanism, we use proto files with .proto extensions where we write the data to be serialized. The proto files contain message types in which we define our data.

Let’s create a simple proto file with a message type in comparison with JSON type for better understanding. The simpler JSON file will be looking as below

{
is_logged_in: true,
user_name: "Android"
}

Now let's create a proto file format for this

syntax = "proto3";message UserData{
bool is_logged_in = 1;
string user_name = 2;
}

For each field, we need to define Field Types , Field Numbers , Field Rules ,etc. To learn more about Protobufs checkout Google guide for Protobufs

Note: The proto files can be compiled to generate the code as per the user’s programming language.

Enough of talks let’s move to the coding part…

Implementation

Let's create an UserDataStore where we store data related to the user.

Step 1

Adding a simple dependency is not sufficient here. We need to

  • add the Protobuf plugin and configure the Protobuf
  • add dependencies of Protobuf and Proto DataStore.

Step 2

As we are done with Gradle set-up let’s move to the creation of a proto file with required fields. Create a proto directory under app/src/main/. Let’s add the proto file to the proto directory with the name user_store with the extension of .proto .

Step 3: Adding the content in the proto file.

We need to define the version, package, java_multiple_files options and define our message object with the required fields. Let’s store two fields is_logged_in of type Boolean and user_name of type string. As we have already seen in the Protobuf section the syntax of the proto file let’s follow the same

syntax = "proto3";

option java_package = "com.sample.android_sample_preference_datastore";
option java_multiple_files = true;

message UserStore {
bool is_logged_in = 1;
string user_name = 2;
}

We can add multiple messages where each individual message has a class generated. If we have app-related data we can create AppData and for user-related stuff UserStore, etc

Step 4

Rebuild your project and check that UserStore.java should be generated under app/build/generated/source/proto or double shift and check with the name UserStore . Just have an overview look-up of UserStore where it has different methods for storing, retrieving data, creating instances of UserStore and other stuff.

Step 5

Now it’s time to create the serializer. Let’s create a class that implements Serializer<T>, where T is the message type defined in the proto file. This Serializertells the datastore how to read and write the data type we defined in the proto file. Let's create UserStoreSerializer

Note: It would be best practice to run the blocks of readFrom and writeTo inside withContext(Dispatchers.IO) { } and handle exceptions in both the case as the methods readFrom and writeTo may throw IO Exception. We need to define multiple serializers if have multiple message types

Step 6: Creating the Proto DataStore instance

We can use the dataStore delegate for the creation of Datastore instance. The delegate needs 2 mandatory inputs those are name and the serializer

The dataStore delegate ensures that we have a single instance of DataStore with that name in our application.

Step 7: Read operation from the proto data store instance

Same as we did in the case of Preference Datastore we can make use of DataStore.data to expose a Flow of the specific property from the stored instance state.

To handle exceptions while reading wrap with a catch block

Step 8: Wite operation on the proto data store instance

We have updatedata() functions that update the data transactionally in an atomic read-modify-write operation. We fetch the current state of the property then write on it and save it.

Example

Now let's check them together in a simple example as we have done in the case of preference data store. Let’s store and fetch a boolean(user logged-in state)using a repository pattern inside an activity. In this example repository pattern is nothing but a simple interface and class implementing the interface where we do store and fetch operations on protoDataStore instance. For keeping this simple let’s do a manual injection of protoDataStore instance to repository implementation. As we keep observing the flowable emitted by the preference data store the data will be automatically changed

Step 1

Let’s create a simple XML with two buttons for log-in and log-out.

Step 2

Let’s create an Activity where we inflate the above layout and with one click of the Login button we save the state for user log-in as true and whereas on click of other we save false. Also, we create an instance of proto DataStore andwill keep observing the user log-in state. Based on the state we append text and background-color

We need to add lifecycle-runtime-ktx dependency to make use of lifecycleScope

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"

Step 3

Let’s create ProtoUserRepo with two methods for save and fetch of user-login state

Step 4

Let’s provide ProtoUserRepoImpl an implementation to ProtoUserRepo

That’s all we are done now run the app and check the output

Output

1*itBySfJfqK5iF54JOdwdtg.gif?q=20
android-jetpack-proto-datastore-a11ff8edcda8

If you have any issues while executing the code snippets please check out the GitHub repo for handy access.

Summary

Datastores are advancement solutions for storage. Proto Datastore uses proto buffers and offers type safety. Protobufs are for serializing and de-serializing the data. As it doesn’t have a stable release yet think twice before using it in apps. . So give it a try…

Thank you for reading…


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK