Hong Hong - 1 month ago 12
Android Question

What happens to the existing data of Android user preferences when preferences structure changes in a new version?

For example, if a few entries of preferences are added or deleted, how does Android handle the existing preference data when the app is updated with the new preferences structure?

I am sorry for this rudimentary question, but my diligent search and reading could not find the answer.

Answer

The shared preferences are stored in the xml file in the folder data/data/your.application.package/shared_prefs/. The file is called your.application.package_preferences.xml;

When you retrieve the shared preferences, you call the Context.getSharedPreferences method. It creates the SharedReferences object and invokes the SharedReferences.startLoadFromDisk method.

If you open this method, you will see that the xml file with preferences (mFile) is parsed and preferences are loaded into the memory storage (map).

BufferedInputStream str = new BufferedInputStream(new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);

From then you will always read your preferences from the memory. More exactly from the private Map<String, Object> mMap variable. Also the application can call the startReloadIfChangedUnexpectedly method and if the underlying file has been changed, it will be parsed and a new HashMap will be created.

As to your question, there are the following cases:

  1. You added a preference item in a new version. Then the default value specified as the second parameter will be returned. Note: the attribute android:defaultValue is not used, so be aware.

    String v = (String)mMap.get(key); // not found => v = null

    return v != null ? v : defValue; // return defValue

  2. You removed a preference item in a new version. The xml file and map object will contain some redundant data, but it will be fixed when the user saves preferences next time.

  3. You changed the key of a preference item to some key which wasn't used. Then the default value will be returned. The same result as p.1.

  4. You removed one preference item (with the key pref1_key, for example) and changed the key of another item so that it refers to the first item (from pref2_key to pref1_key). Then the second preference item will have the value of the first item.

  5. You changed a type of a preference item (from boolean to int, for example). Then it will throw the CastException because of this and similar code: (Integer)mMap.get(key);. But you can change, for example, EditTextPreference to ListPreference, because they both have the String type.

Maybe there are some more test cases, but so far I've made up only 5.

Also here is the example of the preferences file with ListPreference, EditTextPreference and CheckBoxPreference:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="pref_theme_key">THEME_LIGHT</string>
    <string name="pref_homepage_key">test</string>
    <boolean name="pref_display_name_key" value="true" />
</map>