What is the most appropriate way to store user settings in Android application

Viewed 157095

I am creating an application which connects to the server using username/password and I would like to enable the option "Save password" so the user wouldn't have to type the password each time the application starts.

I was trying to do it with Shared Preferences but am not sure if this is the best solution.

I would appreciate any suggestion on how to store user values/settings in Android application.

16 Answers

In general SharedPreferences are your best bet for storing preferences, so in general I'd recommend that approach for saving application and user settings.

The only area of concern here is what you're saving. Passwords are always a tricky thing to store, and I'd be particularly wary of storing them as clear text. The Android architecture is such that your application's SharedPreferences are sandboxed to prevent other applications from being able to access the values so there's some security there, but physical access to a phone could potentially allow access to the values.

If possible I'd consider modifying the server to use a negotiated token for providing access, something like OAuth. Alternatively you may need to construct some sort of cryptographic store, though that's non-trivial. At the very least, make sure you're encrypting the password before writing it to disk.

About the simplest way to store a single preference in an Android Activity is to do something like this:

Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();

If you're worried about the security of these then you could always encrypt the password before storing it.

I know this is a little bit of necromancy, but you should use the Android AccountManager. It's purpose-built for this scenario. It's a little bit cumbersome but one of the things it does is invalidate the local credentials if the SIM card changes, so if somebody swipes your phone and throws a new SIM in it, your credentials won't be compromised.

This also gives the user a quick and easy way to access (and potentially delete) the stored credentials for any account they have on the device, all from one place.

SampleSyncAdapter is an example that makes use of stored account credentials.

This is a supplemental answer for those arriving here based on the question title (like I did) and don't need to deal with the security issues related to saving passwords.

How to use Shared Preferences

User settings are generally saved locally in Android using SharedPreferences with a key-value pair. You use the String key to save or look up the associated value.

Write to Shared Preferences

String key = "myInt";
int valueToSave = 10;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(key, valueToSave).commit();

Use apply() instead of commit() to save in the background rather than immediately.

Read from Shared Preferences

String key = "myInt";
int defaultValue = 0;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int savedValue = sharedPref.getInt(key, defaultValue);

The default value is used if the key isn't found.

Notes

  • Rather than using a local key String in multiple places like I did above, it would be better to use a constant in a single location. You could use something like this at the top of your settings activity:

      final static String PREF_MY_INT_KEY = "myInt";
    
  • I used an int in my example, but you can also use putString(), putBoolean(), getString(), getBoolean(), etc.

  • See the documentation for more details.

  • There are multiple ways to get SharedPreferences. See this answer for what to look out for.

First of all I think User's data shouldn't be stored on phone, and if it is must to store data somewhere on the phone it should be encrypted with in the apps private data. Security of users credentials should be the priority of the application.

The sensitive data should be stored securely or not at all. In the event of a lost device or malware infection, data stored insecurely can be compromised.

I use the Android KeyStore to encrypt the password using RSA in ECB mode and then save it in the SharedPreferences.

When I want the password back I read the encrypted one from the SharedPreferences and decrypt it using the KeyStore.

With this method you generate a public/private Key-pair where the private one is safely stored and managed by Android.

Here is a link on how to do this: Android KeyStore Tutorial

As others already pointed out you can use SharedPreferences generally but if you would like to store data encrypted it's a bit inconvenient. Fortunately, there is an easier and quicker way to encrypt data now since there is an implementation of SharedPreferences that encrypts keys and values. You can use EncryptedSharedPreferences in Android JetPack Security.

Just add AndroidX Security into your build.gradle:

implementation 'androidx.security:security-crypto:1.0.0-rc01'

And you can use it like this:

String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
    "secret_shared_prefs",
    masterKeyAlias,
    context,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();

See more details: https://android-developers.googleblog.com/2020/02/data-encryption-on-android-with-jetpack.html

Official docs: https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences

This is how I am doing it.

This does not give errors in strict mode.

public class UserPreferenceUtil
{

    private static final String  THEME = "THEME";
    private static final String  LANGUAGE = "LANGUAGE";

    public static String getLanguagePreference(Context context)
    {

        String lang = getPreferenceByKey(context,LANGUAGE);

        if( lang==null || "System".equalsIgnoreCase(lang))
        {
            return null;
        }


        return lang;
    }

    public static void saveLanguagePreference(Context context,String value)
    {
        savePreferenceKeyValue(context, LANGUAGE,value);
    }

    public static String getThemePreference(Context context)
    {

        return getPreferenceByKey(context,THEME);
    }

    public static void saveThemePreference(Context context, String value)
    {
        savePreferenceKeyValue(context,THEME,value);

    }

    public static String getPreferenceByKey(Context context, String preferenceKey )
    {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);

        String value = sharedPreferences.getString(preferenceKey, null);

        return value;
    }

    private static void savePreferenceKeyValue(Context context, String preferenceKey, String value)
    {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString(preferenceKey,value);
        editor.apply();

    }


}

My app does not need a password. However, rather than saving passwords or encrypted passwords, I would save a one-way hash. When the user logs in, I will hash the input the same way and match it with the stored hash.

Related