Why do we need DataStore? - Setting up your android project with - Preference DataStore - Proto/Typed DataStore - Migrating from SharedPreferences to Datastore Contents
a data storage solution Introduction - Comes with two different implementations: - Preferences DataStore, that stores key-value pairs - Proto DataStore, that lets us store typed objects (via protocol buffers) - Stores data asynchronously, consistently, and transactionally - Ideal for small, simple datasets and does not support partial updates or referential integrity.
in a file and performs all data operations on Dispatchers.IO unless specified otherwise. Under the hood - As far as saving data is concerned, Preference DataStore, like SharedPreferences, has no way to define a schema or to ensure that keys are accessed with the correct type. - Proto DataStore lets you define a schema using Protocol buffers allowing persisting strongly typed data. - DataStore provides efficient, cached (when possible) access to the latest durably persisted state. The flow will always either emit a value or throw an exception encountered when attempting to read from disk.
disguise to call on the UI thread. Moreover, there is no mechanism for signaling errors, lack of transactional API - Although Sharedpreferences provides asynchronous APIs for reading changed values, it’s not MAIN-thread-safe. Sometimes becomes a source of ANRs Why do we need DataStore? - DataStore, on the other hand, supports Async API via Kotlin Coroutines and Flow and is safe to use in UI thread - DataStore can signal errors and is safe from runtime exceptions(parsing errors) - Provides type safety - Proto DataStore are comparatively faster, smaller, simpler, and less ambiguous than XML and other similar data formats - Also, it provides a way to migrate from SharedPreferences :)
serializing structured data. - Protocol buffers currently support generated code in Java, Python, Objective-C, and C++. - Official Document https://developers.google.com/protocol-buffers/docs/proto What are Protocol Buffers?
"androidx.datastore:datastore-core:1.0.0-alpha02" // For ProtoBuf implementation "com.google.protobuf:protobuf-javalite:3.10.0" } protobuf { protoc { artifact = "com.google.protobuf:protoc:3.10.0" } generateProtoTasks { all().each { task -> task.builtins { java { option 'lite' } } } } } Set up - Proto DataStore To work with Proto DataStore and get Protobuf to generate code for our schema, we'll have to make several changes to the build.gradle file: • Add the Protobuf plugin • Add the Protobuf and Proto DataStore dependencies • Configure Protobuf to generate classes for the serializer
you define your schema in a proto file (e.g filter.proto) in the app/src/main/proto/ directory syntax = "proto3"; option java_package = "com.demo.jetpackdatastore"; message AdFilterPreference { AdType adType = 1; AdCategory adCategory = 2; enum AdType { TYPE_ALL = 0; PAID = 1; FREE = 2; } enum AdCategory { CATEGORY_ALL = 0; AUTOS = 2; ELECTRONICS = 1; } } The AdFilterPreference.java class is generated at compile time from the message defined in the proto file. Build -> Rebuild Project In protobufs, each structure to be saved in Proto DataStore is defined using a message keyword
you’ll also have to implement the DataStore Serializer interface with AdFilterPreference class as type to tell DataStore how to read and write your data type. class AdFilterPreferenceSerializer : Serializer<Filter.AdFilterPreference>{ override fun readFrom(input: InputStream): Filter.AdFilterPreference { try { return Filter.AdFilterPreference.parseFrom(input) } catch (exception: InvalidProtocolBufferException) { throw CorruptionException("Cannot read proto.", exception) } } override fun writeTo(t: Filter.AdFilterPreference, output: OutputStream) { t.writeTo(output) } }
the DataStore builder. - DataStore can automatically migrate from SharedPreferences to DataStore for you. - Migrations are run before any data access can occur in DataStore. - Migration must have succeeded before DataStore data returns any values and before DataStore.updateData() can update the data. - You can use the default SharedPreferencesMigration implementation and just pass in the name used to construct your SharedPreferences. - When migrating to Proto DataStore, you should have to implement a mapping function that defines how to migrate from the key-value pairs used by SharedPreferences to the DataStore schema you defined. Migration
should discontinue using the old SharedPreferences once the code is migrated to DataStore. private val dataStore: DataStore<Preferences> = context.createDataStore( name = "<PREFERENCES_FILE_NAME_TO_MIGRATE>", migrations = listOf(SharedPreferencesMigration(context, "<PREFERENCES_FILE_NAME_TO_MIGRATE>")) ) MIGRATION FROM SHAREDPREFERENCES TO PREFERENCE DATASTORE
{ dataStore.data.first() } Using DataStore in synchronous code - Asynchronously Preloading : Performing synchronous I/O operations on the UI thread is always not recommended. You can overcome these issues by asynchronously preloading the data from DataStore and later synchronous reads using runBlocking() may be faster or may avoid a disk I/O operation altogether if the initial read has completed. override fun onCreate(savedInstanceState: Bundle?) { lifecycleScope.launch { dataStore.data.first() } }