Jetpack DataStore is a new data storage solution and an alternative improvement upon Shared Preferences.
Jetpack DataStore library uses Kotlin coroutines and Flow for easier asynchronous reads and writes.
This library allows you to store key-value pairs or typed objects with protocol buffers.
Protocol buffers are Google’s extensible mechanism for serializing structured data.
Types of DataStore
There are different approaches in DataStore :
SharedPreferences DataStore
- It uses key-value pairs to store simple data,
- shared preferences has a synchronous API that can appear safe to call on the UI thread, but which actually does disk I/O operations.
- It throws parsing errors as runtime exceptions.
- This will not require a predefined schema, and it does not provide type safety.
Preferences DataStore
- Preferences DataStore is more powerful than SharedPreferences since it automatically handles proper asynchronous reads and writes
- It uses key-value pairs to store simple data, just like SharedPreferences.
- This will not require a predefined schema, and it does not provide type safety.
Proto DataStore
- It stores data as instances of a custom data type.
- For storing custom data types we need to define the schema.
- With custom datatype, and it provides type safety.
If you are interested to use Jetpack DataStore for storage in your application then this blog will help you to achieve your goal.
Initial setup
First, start with adding DataStore dependency in your Gradle file
1 2 3 4 5 |
dependencies { // Preferences DataStore implementation "androidx.datastore:datastore-preferences:1.0.0-alpha02" } |
if you want to use Proto DataStore, don’t forget to add the below dependency :
1 2 |
// Proto DataStore implementation "androidx.datastore:datastore-core:1.0.0-alpha02" |
Using DataStore
if you are using proto DataStore schema is defined in the proto file on app/src/main/proto/ directory.
Create a DataStore
Create a Preferences DataStore
Context.createDataStore() extension function is used to create an instance of Preferences DataStore with a mandatory name parameter which is the name of the Preferences DataStore.
1 2 3 |
val dataStore: DataStore<Preferences> = context.createDataStore( name = "customer" ) |
Create a Proto DataStore
- We have to implement the Serializer interface for using Proto DataStore which tells DataStore how to read and write your data type.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
object CustomerSerializer : Serializer<Customer> { override fun readFrom(input: InputStream): Customer { try { return Customer.parseFrom(input) } catch (exception: InvalidProtocolBufferException) { throw CorruptionException("Cannot read proto.", exception) } } override fun writeTo( t: Customer, output: OutputStream) = t.writeTo(output) } |
- Context.createDataStore() extension function create an instance of DataStore.
1 2 3 4 |
val customerProtoDataStore: DataStore<Customer> = context.createDataStore( fileName = "customer.pb", serializer = CustomerSerializer ) |
Here two parameters fileName is the name of the file on app/src/main/proto/ which stores the data and serializer is the name of the serializer class which is CustomerSerializer in my case.
Read DataStore
Preferences DataStore Read
preferencesKey() is used with key for each value that needs to be store in DataStore.
1 2 3 4 5 6 |
val CUSTOMER_TOKEN = preferencesKey<Int>("customer_token") val customerTokenFlow: Flow<Int> = dataStore.data .map { preferences -> preferences[CUSTOMER_TOKEN] ?: 0 //Type saftey is not present } |
Proto DataStore Read
1 2 3 4 |
val customerTokenFlow: Flow<Int> = customerProtoDataStore.data .map { customer -> customer.customerToken //this property is generated from the proto schema } |
Write DataStore
Preferences DataStore Write
edit() function is to updates the data in a DataStore.
1 2 3 4 5 6 |
suspend fun updateCustomerToken() { dataStore.edit { customer -> val currentCustomerToken = customer[CUSTOMER_TOKEN] ?: 0 customer[CUSTOMER_TOKEN] = currentCustomerToken + 100 } } |
Proto DataStore Write
For updating the stored data object use updateData() function.
1 2 3 4 5 6 7 |
suspend fun updateCustomerToken() { customerProtoDataStore.updateData { currentCustomerToken -> currentCustomerToken.toBuilder() .setCustomerTtoken(currentSettings.customerToken + 1) .build() } } |