DEV Community

azattech
azattech

Posted on

Jetpack DataStore and How to implement it.

Jetpack DataStore is a new improved library and built on Coroutines and Flow which aims to replace the SharedPreferences. So What are the advantages over SharedPreferences; Data is stored asynchronously, consistently, and transactionally.

If we look a little deeper into our topic;

  • DataStore provides asynchronous API(via Flow) for storing and reading data but SharedPreferences provides async API only for reading data.

  • DataStore is safe to call on the UI thread because it uses Dispatchers.IO, but SharedPreferences blocks the UI thread. apply() and commit() have no mechanism of signaling errors and apply() will block the UI thread on fsync() often becoming a source of ANRs.

  • DataStore is safe from runtime exceptions, but SharedPreferences throws parsing errors in runtime exceptions.

And also DataStore has two different implementations:
Preferences DataStore and Proto DataStore

Preferences DataStore – stores and accesses data via key-value pairs like SharedPreferenecs, but this implementation does not provide type safety.

Proto DataStore – stores data as custom objects and requires to define a schema using protocol buffers. Also, it provides type safety.

Note: Today we're just looking at the implementation of the Preferences DataStore.

Screenshot_2020-09-12 Prefer Storing Data with Jetpack DataStore

First of all, we need to add some dependencies;

    // Preferences DataStore
    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01" 

    // Lifecycle components
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" 
Enter fullscreen mode Exit fullscreen mode

Ok, Let's suppose we store a user's informations which are name and surname. Her is our UI;

nameandsurname

Next, we're gonna create a named UserInfo class which takes a context in the constructor.

class UserInfo(context: Context) { 
Enter fullscreen mode Exit fullscreen mode

below, we create a dataStore and we need to give it a name.

class UserInfo(context: Context) {

    val dataStore = context.createDataStore("user_info")

}
Enter fullscreen mode Exit fullscreen mode

Then we should create some keys, so to do this we're gonna create a companion object.

we create two keys which are named USER_NAME_KEY && USER_SURNAME_KEY, they're gonna be equal to preferencesKey of type String and then we need to give them the actual name USER_NAME && USER_SURNAME.

 class UserInfo(context: Context) {

    val dataStore = context.createDataStore("user_info")

    companion object {
        val USER_NAME_KEY = preferencesKey<String>("USER_NAME")
        val USER_SURNAME_KEY = preferencesKey<String>("USER_SURNAME")
    }
} 
Enter fullscreen mode Exit fullscreen mode

Next, we create a suspend function to save our data and it takes two arguments. We call dataStore.edit inside here we store values.

  suspend fun saveUserInfo(name: String, surname: String ) {
        dataStore.edit {
            it[USER_NAME_KEY] = name
            it[USER_SURNAME_KEY] = surname
        }
    }
Enter fullscreen mode Exit fullscreen mode

So, that will take care of storing data to the key we have assigned it.

Now we create two flow which will help us to retrieve data. And we map them to our keys also we check if they're null we return an empty String ("")

val userNameFlow: Flow<String> = dataStore.data.map {
        it[USER_NAME_KEY] ?: ""
    }

val userSurnameFlow: Flow<String> = dataStore.data.map {
        it[USER_SURNAME_KEY] ?: ""
    } 
Enter fullscreen mode Exit fullscreen mode

Okay until now we're done with UserInfo. Now we're going to MainActivity.

First, we create a lateinit var for UserInfo and 2 variables for name and surname.

 lateinit var userInfo: UserInfo

 var name = ""
 var surname = ""
Enter fullscreen mode Exit fullscreen mode

And we're going to onCreate method to instantiate userInfo and write two methods that retrieve and save data.

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        userInfo = UserInfo(this)

        saveData()

        observeData()
    }
Enter fullscreen mode Exit fullscreen mode

After this one, we're going to saveData()
and inside here, as you know we created a suspend function in UserInfo class. So, this has to be done in CoroutineScope. We're just gonna call GlobalScope.launch and inside this block code we call saveUserInfo() method.

 private fun saveData() {
        buttonSave.setOnClickListener {
            name = etName.text.toString()
            surname = etSurname.text.toString()

            GlobalScope.launch {
                userInfo.saveUserInfo(name, surname)
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

After saving data we're going to observe our LiveDatas and retrieve them to write to our TextViews.

private fun observeData() {
        userInfo.userNameFlow.asLiveData().observe(this, {
            name = it
            tvName.text = "Saved name: $it"
        })

        userInfo.userSurnameFlow.asLiveData().observe(this, {
            surname = it
            tvSurname.text = "Saved surname: $it"
        })
    }
Enter fullscreen mode Exit fullscreen mode

Alt Text

Alt Text

  • In additionally, as I mentioned before, DataStore is safe from runtime exceptions it reads data from a file, IOExceptions are thrown when an error occurs while reading data so, we can handle these exceptions before map()
val userNameFlow: Flow<String> = dataStore.data
        .catch { exception ->
            if (exception is IOException) {
                Log.d("DataStore", exception.message.toString())
                emit(emptyPreferences())
            } else {
                throw exception
            }
        }.map {
            it[USER_NAME_KEY] ?: ""
        }

Enter fullscreen mode Exit fullscreen mode

Yeah, that's all.

Top comments (0)