Hello everyone, In this article we will learn about the new Jetpack component DataStore for storing the data like shared preference. Jetpack DataStore is a data storage solution. It allows us to store key-value pairs (like SharedPreferences) or typed objects with protocol buffers. DataStore uses Kotlin and Coroutines + Flow to store data synchronously with consistency and transaction support. In short, it’s the new data storage solution which is the replacement of SharedPreferences.
What Data Store?
- From the definition it is clear that used the Kotlin core feature like Coroutines + Flow to store the data.
- SharedPreference has some limitation like it is relies on Synchronous which is not safe for main thread, and we need to manually switch to thread if needed. while Data Store is safe for UI thread because it is use Coroutines Dispatchers.IO thread.
- The best of all is that as it uses Kotlin flow which can be used as liveData using asLiveData() or Flow, so in this way, you can also make it lifecycle aware.
- SharedPreference does not throw IO exception if something happen wrong.
- SharedPreference is not type safe, while Data Store is type safe by using protocol buffer.
How to use the Data Store?
Let’s check the implementation part to use the Data Store.
First we need to add this dependency to our app.gradle file
// Preferences DataStore implementation "androidx.datastore:datastore-preferences:1.0.0"
Ok great, let say an example of Data Store. In our app we use multi language support based on county. User can select any language corresponding to prefer language. This user preference for language need to save to use in the app. As we know that Data Store use the Coroutines Flow then we need to create the set and get function of update state should be suspended. Let me create class of LanguageManager.kt.
class LanguageManager(private val context: Context) { val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "language_pref") companion object { val LAST_LANGUAGE_KEY = stringPreferencesKey(name = "language") } suspend fun setLanguagePref(language: String) { context.dataStore.edit { preference -> preference[LAST_LANGUAGE_KEY] = language } } val getLanguagePref: Flow<String> = context.dataStore.data .map { preferences -> preferences[LAST_LANGUAGE_KEY] ?: "" } }
Here I use my preferenceDataStore name is “language_pref” and I use the preference to store any data in key value pair which I use the key “language”. Here I used tow function, one is use for store the data in DataStore and another one use to get the data from the DataStore.
Now I will use this LanguageManager instance in my ViewModel class with viewModel Scope to get aware the life cycle.
class LanguageViewModel(private val languageManager: LanguageManager) : ViewModel() { private val _languageLiveData = SingleLiveEvent<Resource<String>>() val languageLiveData: SingleLiveEvent<Resource<String>> get() = _languageLiveData fun setLanguagePref(language: String) { viewModelScope.launch { languageManager.setLanguagePref(language) } } fun getLanguage() { viewModelScope.launch { try { languageManager.getLanguagePref.collect { _languageLiveData.value = Resource.success(it) } } catch (e: Throwable) { _languageLiveData.value = Resource.error(e.localizedMessage.toString(), null) } } } }
Let me use this viewModel in our activity class.
class LanguageActivity: AppCompatActivity() { private var viewModel: LanguageViewModel?= null private var selectedLanguage: String?= null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_language) viewModel = ViewModelProviders.of(this, viewModelFactory { LanguageViewModel( LanguageManager(this) ) }).get(LanguageViewModel::class.java) setUpObserver() viewModel?.getLanguage() button.setOnClickListener { if (selectedLanguage == LanguagePref.HINDI.name) { viewModel?.setLanguagePref(LanguagePref.ENGLISH.name) }else{ viewModel?.setLanguagePref(LanguagePref.HINDI.name) } viewModel?.getLanguage() } } private fun setUpObserver(){ viewModel?.languageLiveData?.observe(this, Observer { when(it.status){ Status.SUCCESS -> { selectedLanguage = it.data textView.text = selectedLanguage } Status.ERROR -> { selectedLanguage = "" } } }) } }
Here while click on button, we are storing the value of selected language to DataStore and getting the value back by using the live data change.
That’s it 😍. We have implemented PreferencesDataStore now. 😍 In my next article we will learn about the migration from SharedPreference to DataStore as well as Proto DataStore. Happy Coding.
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com