Beyond preferences
Tags: android, jetpack, kotlin
Published: 2021-07-23 16:50:54

As Android developers we are used to having a choice. There are always several approaches to do *something*. Now, while being able to choose is generally a very good thing, it can make developer life pretty challenging. First, you need to know that there are alternatives. Second, you need to know them good enough to make a sound decision. And third, well, there is `@Deprecated`.

### In the beginning

Storing and retrieving user settings has always been very simple on Android. `android.preference.PreferenceManager` has been around since API level 1.

```kotlin
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val current = prefs.getBoolean("hasBeenSet", false)
println(current)
prefs.edit().putBoolean("hasBeenSet", !current).apply()
```

Only trouble is... We shouldn't use it any longer.

![A deprecation note](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0jmlpv8zcuznm7xpmgu.png)
 
Let's briefly look at the advertised successor, `androidx.preference`. As you will see shortly, it is straightforward to use in most cases. The first step is to include the library in your *build.gradle*:

```groovy
implementation 'androidx.preference:preference-ktx:1.1.1'
```

If you now change the `import` statement to `androidx.preference.PreferenceManager` the above example works without further changes. But there is more to preferences, for example the integration in the user interface of an app. 

These classes (`android.preference.CheckBoxPreference`, `PreferenceScreen` and so on) have been deprecated as well, so we need to use the replacements provided by *Jetpack Preference*. The migration of basic preferences types is simple, yet you may face some effort when you have custom classes. I am not going into detail here, though. Because while this chapter might be closed (we do have a new king now) on other platforms, on Android it is not. Allow me to present...

### Jetpack Security

Just because they are so easy to read and write, preferences have been picked for scenarios in which they are not the best choice. In other words:

**Do not store a password using the preferences api**

Preferences are written to xml files, which can be read easily once an attacker has gained access to the internal files directory of an app. Things would be much harder if that file was encrypted. That's what (among other things) *Jetpack Security* can do. The docs say:

> Safely manage keys and encrypt files and sharedpreferences.

Let's take a look.

```groovy
implementation 'androidx.security:security-crypto:1.1.0-alpha03'
```

Here's how to set things up:

```kotlin
val masterKey = MasterKey.Builder(this)
  .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
  .build()
val prefs = EncryptedSharedPreferences.create(this,
  "secret_shared_prefs",
  masterKey,
  EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
  EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
```

I am not going into detail. Basically you obtain a master key and feed it to `EncryptedSharedPreferences.create()`. The important thing is: afterwards you can access preferences in the same way like I showed in the beginning, using `getBoolean()`, `edit()` and `putBoolean()`.

Now, isn't this cool?

It is.

But it's not the end of the story. 

### Jetpack DataStore

Back in September 2020 the Android Developers Blog featured a post called [Prefer Storing Data with Jetpack DataStore](https://android-developers.googleblog.com/2020/09/prefer-storing-data-with-jetpack.html). It starts by saying:

> Welcome Jetpack DataStore, now in alpha - a new and
> improved data storage solution aimed at replacing
> SharedPreferences. Built on Kotlin coroutines and Flow,
> DataStore provides two different implementations: **Proto
> DataStore**, that lets you store **typed objects** (backed
> by [protocol buffers](https://developers.google.com/protocol-buffers)) and **Preferences DataStore**, that stores
> **key-value pairs**. Data is stored asynchronously,
> consistently, and transactionally, overcoming most of the
> drawbacks of SharedPreferences.

If you are interested in what these drawbacks are, please be sure to read the post. It's both interesting and enlightening. My point is: there is yet another way to read and write key-value-pairs. As it is set to replace shared preferences, let's see how to use *Preferences DataStore*.

```groovy
implementation "androidx.datastore:datastore-preferences:1.0.0-rc02"
```

Here's how to prepare a data store. The docs say:

> Use the property delegate created by `preferencesDataStore`
> to create an instance of `Datastore<Preferences>`. Call it
> once at the top level of your kotlin file, and access it
> through this property throughout the rest of your
> application. This makes it easier to keep your `DataStore`
> as a singleton.

```kotlin
val Context.dataStore by preferencesDataStore("user_preferences")
```

My example is accessing a boolean value. We define it like this:

```kotlin
val key = booleanPreferencesKey("hasBeenSet")
```

Here is how to prepare a read:

```kotlin
val flow: Flow<Boolean> = dataStore.data
  .map { currentPreferences ->
    currentPreferences[key] ?: false
  }
```

To get the value we use

```kotlin
lifecycleScope.launch {
  println(flow.first())
```

I you the same coroutine to change the value:

```kotlin
dataStore.edit { settings ->
  val currentCounterValue = settings[key] ?: false
  settings[key] = !currentCounterValue
}
```

Granted, this may look strange at first sight. But please keep in mind that we are witnessing a strong movement towards coroutines in general and flows in particular. So our app code will inevitably become more asynchronous.

### Conclusion

The old preferences api has been deprecated for quite a while, so we should now try to get rid of it. Google advocates Jetpack Datastore quite a bit, so it may be a safe bet to switch to it. On the other hand, the other alternatives I have presented, work well, too, and may feel a little more common. Anyway, the choice is yours. Which one would you pick? Please share your thoughts in the comments.

---

[Source](https://github.com/tkuenneth/android-demos/tree/master/misc/StoringDataDemo)