Step #7: Retrieving Our Preference

All through this work, we have been passing around a URL as a parameter. We are getting the URL from the user in our PrefsFragment, but we need a way to get that value (or a default value) into our main code. And, since this involves disk I/O, we should set up another repository with a suspend function that can handle loading that data for us.

Right-click over the com.commonsware.todo.repo package in the java/ directory and choose “New” > “Kotlin File/Class” from the context menu. For the name, fill in PrefsRepository, and choose “Class” for the kind. Press Enter or Return to create the class. Then, replace the class contents with:

package com.commonsware.todo.repo

import android.content.Context
import androidx.preference.PreferenceManager
import com.commonsware.todo.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class PrefsRepository(context: Context) {
  private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
  private val webServiceUrlKey = context.getString(R.string.web_service_url_key)
  private val defaultWebServiceUrl =
    context.getString(R.string.web_service_url_default)

  suspend fun loadWebServiceUrl(): String = withContext(Dispatchers.IO) {
    prefs.getString(webServiceUrlKey, defaultWebServiceUrl) ?: defaultWebServiceUrl
  }
}

Given a Context constructor parameter, we set up three properties:

SharedPreferences gives us read/write access to the preferences. Those preferences are stored on disk in an XML file. The first time we try reading (or writing) a preference, the SharedPreferences will load that XML file into memory. Therefore, the loadWebServiceUrl() function is a suspend function, so we ensure that loading and parsing that XML happens on a background thread.

To read a preference, you call a typed getter method, such as getString(), on the SharedPreferences object. This takes two parameters:

getString() is marked as potentially returning null. That is because you could pass null as the default value, in which case getString() will return null if there is no value for the preference defined yet. getString() should not return null if you provide a non-null default value… but the Kotlin compiler has no way of knowing this. Since we need some URL to try, loadWebServiceUrl() is set up to return String, not String?. So we cannot just return the String? that we get back from getString(). We could use the Kotlin !! operator to force the type to be non-nullable. Here, we use the Elvis operator to say “OK, if getString() returns null unexpectedly, use our default value”.

Then, go into ToDoApp and add another line to our module closure:

    single { PrefsRepository(androidContext()) }

This will make a PrefsRepository available to other components via Koin.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.