Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Androidx Preferences Library vs DataStore preferences

I had previously replaced SharedPreferences in my app with the new DataStore, as recommended by Google in the docs, to reap some of the obvious benefits. Then it came time to add a settings screen, and I found the Preferences Library. The confusion came when I saw the library uses SharedPreferences by default with no option to switch to DataStore. You can use setPreferenceDataStore to provide a custom storage implementation, but DataStore does not implement the PreferenceDataStore interface, leaving it up to the developer. And yes this naming is also extremely confusing. I became more confused when I found no articles or questions talking about using DataStore with the Preferences Library, so I feel like I'm missing something. Are people using both of these storage solutions side by side? Or one or the other? If I were to implement PreferenceDataStore in DataStore, are there any gotchas/pitfalls I should be looking out for?

like image 436
fafcrumb Avatar asked Dec 21 '20 17:12

fafcrumb


People also ask

What is Preferences DataStore?

Preferences DataStore uses key-value pairs to store smaller datasets, without defining the schema upfront. This might remind you of SharedPreferences , but only in the way it structures your data models. There are a variety of benefits brought by DataStore over its SharedPreferences predecessor.

What is Androidx preference?

preference. Kotlin |Java. The Preference library allows you to build interactive settings screens, without needing to handle interacting with device storage or managing the user interface.

Is jetpack DataStore a replacement for SharedPreferences?

The DataStore from Jetpack is a replacement for SharedPreferences that addresses most of its shortcomings. As it doesn't have a stable release yet think twice before using it in apps. It also provides easy migration from existing SharedPreferences.


1 Answers

For anyone reading the question and thinking about the setPreferenceDataStore-solution. Implementing your own PreferencesDataStore with the DataStore instead of SharedPreferences is straight forward at a glance.

class SettingsDataStore(private val dataStore: DataStore<Preferences>): PreferenceDataStore() {

    override fun putString(key: String, value: String?) {
        CoroutineScope(Dispatchers.IO).launch {
            dataStore.edit {  it[stringPreferencesKey(key)] = value!! }
        }
    }

    override fun getString(key: String, defValue: String?): String {
        return runBlocking { dataStore.data.map { it[stringPreferencesKey(key)] ?: defValue!! }.first() }
    }

    ...
}

And then setting the datastore in your fragment

@AndroidEntryPoint
class AppSettingsFragment : PreferenceFragmentCompat() {

    @Inject
    lateinit var dataStore: DataStore<Preferences>

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        preferenceManager.preferenceDataStore = SettingsDataStore(dataStore)
        setPreferencesFromResource(R.xml.app_preferences, rootKey)
    }
}

But there are a few issues with this solution. According to the documentation runBlocking with first() to synchronously read values is the preferred way, but should be used with caution.

Make sure to setpreferenceDataStore before calling setPreferencesFromResource to avoid loading issues where the default implementation (sharedPreferences) will be used for initial loading.

A couple weeks ago on my initial try to implement the PreferenceDataStore, I had troubles with type long keys. My settings screen was correctly showing and saving numeric values for an EditTextPreference but the flows did not emit any values for these keys. There might be an issue with EditTextPreference saving numbers as strings because setting an inputType in the xml seems to have no effect (at least not on the input keyboard). While saving numbers as strings might work, this also requires reading numbers as strings. Therefore you lose the type-safety for primitive types.

Maybe with one or two updates on the settings and datastore libs there might be an official working solution for this case.

like image 65
asuras Avatar answered Nov 06 '22 03:11

asuras