1

In my application, I am using SharedPreferences to store some user preferences. The application was not obfuscated (-dontobfuscate in the proguard file).

Now in the next version of the application, I want to enable obfuscation. When I try this, the application returns NullPointerException while reading the SharedPreferences data from the previous version of the application. The error log is not helpful because the code is already obfuscated and it does not provide meaningful information. However, while trying in the debug mode I found the crash may be due to null context which is a static variable in the code! That should not be the case because the application works file if SharedPreferences were not there already.

Is there any way the app can still read the SharedPreferences data from unobfuscated version?

Writing / reading the SharedPreferences is pretty standard: Writing:

SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor prefsEditor = mPrefs.edit();
    prefsEditor.putString("userUnitsOption", "C");
    //apply the storage
    prefsEditor.apply();

Reading:

final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
        return mPrefs.getString("userUnitsOption", "A");
user846316
  • 6,037
  • 6
  • 31
  • 40
  • Can you show some code on how you are you reading/writing from your shared preferences. – mononz Apr 02 '18 at 05:25
  • That's pretty standard: SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor prefsEditor = mPrefs.edit(); prefsEditor.putString("userUnitsOption", "C"); //apply the storage prefsEditor.apply(); – user846316 Apr 02 '18 at 05:26
  • 1
    ahk, looks like you are using `getDefaultSharedPreferences`. I seem to always use `getSharedPreferences` because it forces to you name your preferences location. Found something here that may help https://stackoverflow.com/a/6310080/6407116 The default shared pref name may be changed during obfuscation by proguard? Might be able to reference it by its current name `getSharedPreferences("your_existing_default_shared_pref_name", Context.MODE_PRIVATE)` – mononz Apr 02 '18 at 05:33
  • Or maybe just this https://stackoverflow.com/questions/4776073/android-app-preferences-are-being-cleared-automatically – mononz Apr 02 '18 at 05:42
  • Since your app crashes in debug build too, can you try to initialize/assign the context just before PreferenceManager.getDefaultSharedPreferences() ? At least we will be certain that Obfuscation is the culprit. – Sagar Apr 02 '18 at 06:12

2 Answers2

0

If you want to use shared preferences in Android then use it as given below :

First, you have to store your preferences like this :

SharedPreferences.Editor editor = getSharedPreferences("mySharedPref", MODE_PRIVATE).edit(); 
            editor.putString("myName", "abc");
            editor.apply();

Now, To read or get those stored shared preferences write code as given below :

SharedPreferences prefs = MainActivity.this.getSharedPreferences("mySharedPref", MODE_PRIVATE); // Here  MainActivity.this represent the context. So you can use your context in place  of MainActivity.this
            String strName = prefs.getString("myName","defaultName");
Hemant N. Karmur
  • 840
  • 1
  • 7
  • 21
0

In Kotlin,

Usage:
private val sharedPref = defaultPrefs(this)

To Save Data--> sharedPref[KEY] = *String data to save*

To Get Data --> val userDetails = sharedPref[KEY, ""]

Create a shared preference helper class like below.

object PreferenceHelper {

    fun defaultPrefs(context: Context): SharedPreferences =
        PreferenceManager.getDefaultSharedPreferences(context)

    fun customPrefs(context: Context, name: String): SharedPreferences =
        context.getSharedPreferences(name, Context.MODE_PRIVATE)

    inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
        val editor = this.edit()
        operation(editor)
        editor.apply()
    }

    /**
     * puts a key value pair in shared prefs if doesn't exists, otherwise updates value on given [key]
     */
    operator fun SharedPreferences.set(key: String, value: Any?) {
        when (value) {
            is String? -> edit { it.putString(key, value) }
            is Int -> edit { it.putInt(key, value) }
            is Boolean -> edit { it.putBoolean(key, value) }
            is Float -> edit { it.putFloat(key, value) }
            is Long -> edit { it.putLong(key, value) }
            else -> throw UnsupportedOperationException("Not yet implemented")
        }
    }

    /**
     * finds value on given key.
     * [T] is the type of value
     * @param defaultValue optional default value - will take null for strings, false for bool and -1 for numeric values if [defaultValue] is not specified
     */
    inline operator fun <reified T : Any> SharedPreferences.get(
        key: String,
        defaultValue: T? = null
    ): T? {
        return when (T::class) {
            String::class -> getString(key, defaultValue as? String) as T?
            Int::class -> getInt(key, defaultValue as? Int ?: -1) as T?
            Boolean::class -> getBoolean(key, defaultValue as? Boolean ?: false) as T?
            Float::class -> getFloat(key, defaultValue as? Float ?: -1f) as T?
            Long::class -> getLong(key, defaultValue as? Long ?: -1) as T?
            else -> throw UnsupportedOperationException("Not yet implemented")
        }
    }
}
Varun Chandran
  • 647
  • 9
  • 23