Streamline SharedPreferences with StoreBox

Streamline SharedPreferences with StoreBox

We may earn a commission for purchases made using our links.

Many applications need to save data in a persistent manner for user preferences or options (e.g. the app’s theme if choices are available or highscores). That’s usually done via the SharedPreferences class, which basically lets developers store and retrieve key-value pairs of data (e.g. “themeToUse” is the key, “DarkTheme” is the value). This data is persistent, which means that it stays available even if the application is killed or the device is restarted.
This article is mostly for developers and users interested in Android development. If you don’t want to know more about a new library which makes the above easier, you might want to skip it.


SharedPreferences: The Usual Way

Developers, how would you normally access or set your app’s preferences? Probably something like this:

SharedPreferences sharedPreferences = PreferenceManager
    .getDefaultSharedPreferences(context);
// Get preference.
sharedPreferences.getBoolean("isUserAUnicorn", true);
// Set preference.
sharedPreferences.edit().putBoolean("isUserAUnicorn", true).commit();

It’s easy enough, but it does get tedious for multiple values…

SharedPreferences sharedPreferences = PreferenceManager
    .getDefaultSharedPreferences(context);
boolean isUserAUnicorn = sharedPreferences.getBoolean(
    "isUserAUnicorn", true);
if (isUserAUnicorn) {
    sharedPreferences.edit()
        .putBoolean("isUserCrazy", isThisTheRealWorld())
        .commit();
    int favoriteColor = sharedPreferences.getInt("favoriteColor",
        0xFF800080);
    skyView.setBackgroundColor(favoriteColor);
} else {
    doSomething();
}

It gets even more tedious when you’re accessing the preferences from different scopes: redundant code, having to remember the default values, etc. A simple solution is to make a single class to access and set your preferences:

public class Config {

     private static Config sInstance;
     private SharedPreferences mSharedPreferences;

     private static final String PREF_IS_USER_A_UNICORN = "isUserAUnicorn";
     private static final String PREF_FAVORITE_COLOR = "favoriteColor";
     private static final String PREF_IS_USER_CRAZY = "isUserCrazy";

     private Config(Context context) {
         mSharedPreferences = PreferenceManager
             .getDefaultSharedPreferences(context);
     }

     public static Config getInstance(Context context) {
         if (sInstance == null)
             sInstance = new Config(context);
         return sInstance;
     }

     public boolean isUserAUnicorn() {
         return mSharedPreferences.getBoolean(PREF_IS_USER_A_UNICORN, true);
     }

     public int getFavoriteColor() {
         return mSharedPreferences.getInt(PREF_FAVORITE_COLOR, 0xFF800080);
     }

     public boolean isUserCrazy() {
         return mSharedPreferences.getBoolean(PREF_IS_USER_CRAZY, false);
     }

     public void setIsUserCrazy(boolean crazy) {
         mSharedPreferences.edit().putBoolean(PREF_IS_USER_CRAZY, crazy)
             .commit();
     }
 }

We can now simplify our example by quite a bit — even though it’s simple enough and doesn’t deal with different scopes, we can still see how much simpler it now is:

Config config = Config.getInstance(context);
if (config.isUserAUnicorn()) {
    config.setIsUserCrazy(isThisTheRealWorld());
    skyView.setBackgroundColor(config.getFavoriteColor());
} else {
    doSomething();
}

 

SharedPreferences: The StoreBox Way

It’s certainly much better, but we can go one step further. StoreBox is a library that aims to make handling SharedPreferences easier and more streamlined, by getting rid of the boilerplate code. In other words, we can modify our Config.class even further to get the following result:

public interface Config {

     private static final String PREF_IS_USER_A_UNICORN = "isUserAUnicorn";
     private static final String PREF_FAVORITE_COLOR = "favoriteColor";
     private static final String PREF_IS_USER_CRAZY = "isUserCrazy";

     @KeyByString(PREF_IS_USER_A_UNICORN)
     boolean isUserAUnicorn(boolean true);

     @KeyByString(PREF_FAVORITE_COLOR)
     int getFavoriteColor(int 0xFF800080);

     @KeyByString(PREF_IS_USER_CRAZY)
     boolean isUserCrazy(boolean false);

     @KeyByString(PREF_IS_USER_CRAZY)
     void setIsUserCrazy(boolean crazy);
 }

The result is shorter, cleaner code without the boilerplate. Our example code is barely changed, too: we just have to modify one line as follows.

// Config config = Config.getInstance(context);
Config config = Config.create(context, Config.class);

You might be doing something more complicated, though — what if you’re not using the default shared preferences, or need a custom operating mode? StoreBox has got you covered in these cases as well, and many more:

  • Opening different types of preferences: you can use one of the following annotations depending on what you want:
    • @DefaultSharedPreferences: use the default shared preferences. This is the default and is used when no annotation is specified.
    • @ActivityPreferences: use the private activity preferences (similar to calling Activity.getPreferences(Context.SOME_MODE), which is equivalent to calling Context.getSharedPreferences("ActivityClassName", Context.SOME_MODE).
    • @FilePreferences: use the preferences indicated by the file name (similar to calling Context.getSharedPreferences("name", Context.SOME_MODE)).
  • Specifying the operation mode to use: you can just pass the mode you want as an argument to the above annotations. For example:
    • @DefaultSharedPreferences(mode = PreferencesMode.SOME_MODE).
    • @ActivityPreferences(mode = PreferencesMode.SOME_MODE).
    • @FilePreferences(mode = PreferencesMode.SOME_MODE).
  • Chaining calls: like SharedPreferences.Editor, StoreBox also allows you to chain calls. Just have your set methods return SharedPreferences.Editor or YourInterfaceName instead of void:
    public interface Config {
    
        // …
    
        @KeyByString(PREF_IS_USER_CRAZY)
        Config setIsUserCrazy(boolean crazy);
    
        @KeyByString(PREF_IS_USER_A_UNICORN)
        Config setIsUserAUnicorn(boolean isAUnicorn);
    }
    
    config.setIsUserAUnicorn(true).setIsUserCrazy(true).apply();
    
  • Some other options are also available, such as calling methods from SharedPreferences or SharedPreferences.Editor, or specifying whether to use SharedPreferences.Editor.commit() or SharedPreferences.Editor.apply() (synchronous vs asynchronous saving).

Get StoreBox

StoreBox is open source and licensed under the Apache v2 license. To get started, the simplest way to add StoreBox to your project is if you’re using Gradle:

compile 'net.orange-box.storebox:storebox-lib:1.0.0'

Otherwise, you can also grab the JAR or add it as a Maven dependency. For more info, check out the full project’s documentation. Also make sure to check out the StoreBox XDA Forum Thread and GitHub project!