12 minutes read

The ability to save data is one of the most crucial parts of any application. Users should be able to comfortably save data and be sure that they'll see it again when they return to the application. In this topic, we will discuss how to save data in an Android application, where it is necessary, and what tools are best in different situations.

Saving does not necessarily need an Internet connection. Let's say we have a list of contacts in our application. This information is stored locally on the phone and can be synchronized with backup services. We can save data in regular files using standard methods of writing and reading files, or we could use a more functional feature: databases.

Ways to save data in Android Application

The Android system offers different storage options within an application. We can store data simply in the internal or external (SD card) storage, we can use the built-in capabilities of SQLite database, and files that store information according to the key-value database. In this section, we will talk about key-value data storage.

Let's remember what a key-value structure is. Also known as Map or Dictionary, key-value pair set is a common data structure that stores objects according to their keys. You can retrieve objects in the structure using keys. Most often, string or integer keys are used.

Let's look at an example of using this structure in Kotlin:

val settings = mutableMapOf("theme" to "dark", "font-style" to "bold", "font-size" to 20)
println(settings["font-size"]) // will be printed: 20

Using this collection, we can perform the following operations: containsValue, containsKey, put, remove, isEmpty, and others.

SharedPreferences is also a persistent key-value storage.

Use cases of SharedPreferences

Now let's look at examples of how to use this repository. In the previous section, we saw an example that illustrates a rather typical use case — we kept the configuration values of our application: theme, style, and font size.

Note that a limited number of data types are supported: Boolean, Float (Double isn't supported!), Int, Long, String, Set<String>. If we want to save complex types, especially with nesting, it is best to save them in separate files.

Example

To learn how to work with this mechanism and understand the benefits it gives us, we will make a small application that supports a very simple change of application theme.

First, let's put the name of our settings in a separate constant in the MainActivity file through which we will receive them later:

const val PREFERENCES_NAME = "my-settings"

Next, let's take a look at some constants in Context class: Context.MODE_PRIVATE makes SharedPreferences file visible only to your application, Context.MODE_WORLD_READABLE allows other applications to read these settings, Context.MODE_WORLD_WRITEABLE allows other applications to write settings of our app. Note that the last two have been deprecated since API 17 for security reasons. Mainly, MODE_PRIVATE is used, so we won't be paying more attention to other modes.

In MainActivity, we need to declare:

lateinit var sharedPreferences: SharedPreferences

In the onCreate function, we need to initialize our SharedPreferences (SP) object:

sharedPreferences = getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)

We can also use getPreferences() if we want to use one SP file for one activity. Accordingly, we don't need to specify the name:

sharedPreferences = getPreferences(Context.MODE_PRIVATE)

In our case, Context.MODE_PRIVATE is responsible for the access mode, which means that only the application has access to the settings.

Practical application

As an example, we will implement a change of theme in the application and restore the installed theme when the application is restarted. Below is the code for the style.xml of the new theme:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="SuperRedTheme" parent="AppTheme">
        <item name="colorPrimary">#E91E63</item>
        <item name="colorPrimaryDark">#C2185B</item>
        <item name="colorPrimaryVariant">#B60A44</item>
        <item name="colorAccent">#FF5252</item>
        <item name="colorOnPrimary">@color/white</item>
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
    </style>
</resources>

To mutate the SP (delete, add, change data), you need to get an Editor from it using the edit() method:

val editor = sharedPreferences.edit()

With the Editor, we can put values by key in our repository. Once we have written all the changes to our settings, we must save them with apply() or commit(). It is better to use apply() in most cases, because it is flushed out asynchronously:

textView.text = sharedPreferences.getString("theme", "default") + " theme"
switch.setOnCheckedChangeListener { _, isChecked ->
    editor.putString("theme", if (isChecked) "red" else "default").apply()
}
switch.isChecked = sharedPreferences.getString("theme", "default") == "red"

Here, we wrote the name of the current app theme in textView getting the value through getString(), and set the listener for the switch that overwrites the value for the "theme" key in our settings file. In the last line, we initialized the switch state when the activity starts.

Besides getString() and putString(), we can also call similar methods for the corresponding supported data types: getInt() and putInt(), getBoolean() and putBoolean(), etc.

The second argument of the getString method is used if there is nothing yet for this key: then, we will get this default string.

We won't go into detail with the themes; just take a look at this code:

override fun getTheme(): Resources.Theme {
    val theme = super.getTheme()
    sharedPreferences = getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
    if (sharedPreferences.getString("theme", "default") == "red")
        theme.applyStyle(R.style.SuperRedTheme, true)
    return theme
}

The method getTheme() is called before onCreate and, depending on the values obtained from our SP repository, it accepts the correct style.

We can also use the same methods as in Map:

editor.remove("theme") // remove data by key
editor.clear() // clear all SP data

In the picture below, you can see a small application implemented in our current topic:

Application on emulated device changes theme by switch

Conclusion

Now you know how to save certain data for our application! States of various interface elements, themes, font values, and other settings: that's where we can use SharedPreferences.

Before we get to the practice tasks, note a few important points about using SP:

  1. Every change in our application data must be placed back in SharedPreferences after the change: otherwise, it will not be saved.

  2. Don't confuse with Intents: SharedPreferences store persistent application state, and Intents carry parameters to application components like Activities.

  3. Remember to apply().

28 learners liked this piece of theory. 7 didn't like it. What about you?
Report a typo