Data Storage in jetpack

harsh shah
3 min readSep 8, 2022

Storing data in android with different techniques

  • SharedPreferences
  • Preferences Data Store
  • Room
  • Proto Data Store

SharedPreference

Generally use for key-value pair. Each SharedPreferences file is managed by the framework and can be private or shared across applications. It’s an interface to allow store/modify/delete data locally.

Save Method :

sharedPref.edit {putString("username","abc")}

Retrieve Method :

val userName = sharedPref.getString("username", "")

Preferences Data Store

Preferences data store is basically replacing old rigid shared preference. It’s quite similar to shared preference. It’s key-value pair data to store but also a safe call to UI thread

Create a datastore instance :

private val Context.userPreferencesDataStore: DataStore<Preferences> by preferencesDataStore( name = “user”)

Create a key for data field :

private val USER_NAME = stringPreferencesKey("username")

Save Data

userPreferencesDataStore.edit { preferences ->        preferences[USER_NAME] = username}

Get the data from data store

override fun getUserFromPreferencesStore(): Flow<User> = userPreferencesDataStore.data    .map { preferences ->        User(            username= preferences[USER_NAME] ?: "")    }

Difference between shared preference vs preference data store

SharedPreferences comes with several drawbacks: a synchronous API that can appear safe to call on the UI thread, no mechanism for signaling errors, lack of transactional API, and more. DataStore is a replacement for SharedPreferences that addresses most of these shortcomings. DataStore includes a fully asynchronous API using Kotlin coroutines and Flow, handles data migration, guarantees data consistency, and handles data corruption

Room

The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. In particular, Room provides the following benefits:

  • Compile-time verification of SQL queries.
  • Convenience annotations that minimize repetitive and error-prone boilerplate code.
  • Streamlined database migration paths.

Components for room database

There are three major components in Room:

  • The database class holds the database and serves as the main access point for the underlying connection to your app’s persisted data.
  • Data entities that represent tables in your app’s database.
  • Data access objects (DAOs) that provide methods that your app can use to query, update, insert, and delete data in the database.

I have already covered this topic in detail. you can go through this link
(https://harsuu942.medium.com/room-database-in-jetpack-compose-7b5fcd1bb6b9)

Proto Data Store

The aim of Proto DataStore is really similar to Preferences DataStore, but the previous one is able to store objects with custom data types
Proto DataStore needs a pre-defined schema with data types and instances that need to be stored. This schema approach helps in providing the type-safety with predefined data types. We need to Define a schema using Protocol buffers

Features of Proto DataStore

  • Easy to integrate compared to Room or any other databases.
  • Easy to store structured data, which is not possible using SharedPreference.
  • Provides hinder-free experience to the user as it works on a separate thread.
  • Frequent changes in the data can be observed in a facile manner using Flow and Coroutines.
  • Protocol buffers get checked for data type mistakes during compilation. Therefore no type-safety errors can occur on run time.

Create .proto file

you need to create a .proto file inside app/main/src/proto folder.
In my case, I have created user_store.proto file inside this folder

syntax = "proto3";
option java_package = "com.example.sample_preference_datastore";
option java_multiple_files = true;

message User {
string image = 1;
string userEmail = 2;
string userId = 3;
string userName = 4;
}

message RecentUsers {
repeated User users = 1; // repeated => list
}

Now Rebuild your project and check that RecentUsers.java should be generated under app/build/generated/source/proto 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.

Create the DataStore Serializer

DataStore how to read and write your data type. Kotlin Serialization supports multiple formats including JSON and Protocol buffers.Let’s create UserStoreSerializer

object UserStore : Serializer<RecentUsers>{
override val defaultValue: RecentUsers = RecentUsers.getDefaultInstance()

override suspend fun readFrom(input: InputStream): RecentUsers {
try {
return RecentUsers.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
}

override suspend fun writeTo(t: RecentUsers, output: OutputStream) =
t.writeTo(output)


}

Create the DataStore

private val USER_DATA_STORE_FILE_NAME = "user_store.pb"

val Context.userDataStore: DataStore<RecentUsers> by dataStore(
fileName = USER_DATA_STORE_FILE_NAME,
serializer = UserStore
)

Save data in protostore

override suspend fun addRecentUser(users: List<User>) {
protoDataStore.updateData { recentUsers: RecentUsers ->

if(recentUsers.toBuilder().usersList.containsAll(users)){
recentUsers
}else {
recentUsers.toBuilder().addAllUsers(users).build()
}
}
}

Read data from protostore

override suspend fun getRecentUsers(): Flow<List<User>> {
return protoDataStore.data.catch { exception ->
if(exception is IOException){
emit(RecentUsers.getDefaultInstance())
}else{
throw exception
}
}.map { protoBuilder ->
protoBuilder.usersList
}
}

Let’s wrap everything & feel to check out https://github.com/harsuu942/RetrofitKotlin this link find API call using the retrofit and response store in the proto data store.

--

--

harsh shah

Mobile Developer (Android , flutter) - 5 Years , Worked on multiple domain like pharma , financial , Ecommerce , BLE .