Support free tutorials









vogella training Training Books



Android Persistence with preferences and files - Tutorial

Lars Vogel

Version 3.2

21.11.2013

File based persistence in Android

This tutorial describes how to save key-value pairs using the preference API in Android. It also explains how to read and write files in Android. It is based on Eclipse 4.3, Java 1.6 and Android 4.3.


Table of Contents

1. File based persistence
1.1. Methods of local data persistence
1.2. Internal vs. external storage
1.3. Application on external storage
2. Preferences
2.1. Storing key-value pairs
2.2. Preference Listener
3. Tutorial: Prerequirements
4. Exercise: Prerequisites
5. Exercise: Preference setting for the RSSfeed
5.1. Create preference file
5.2. Create settings activity
5.3. Connect your settings activity
5.4. Use the preference value to load the RSS feed
5.5. Validate
5.6. Optional: Show the current value in the settings
6. Android File API
6.1. Using the file API
6.2. External storage
7. Support this website
7.1. Thank you
7.2. Questions and Discussion
8. Links and Literature
8.1. Source Code
8.2. Android Resources
8.3. vogella Resources

1. File based persistence

1.1. Methods of local data persistence

Android allows to persists application data via the file system. For each application the Android system creates a data/data/[application package] directory.

Android supports the following ways of storing data in the local file system:

  • Files - You can create and update files

  • Preferences - Android allows you to save and retrieve persistent key-value pairs of primitive data type.

  • SQLite database - instances of SQLite databases are also stored on the local file system.

Files are saved in the files folder and application settings are saved as XML files in the shared_prefs folder.

If your application creates an SQLite database this database is saved in the main application directory under the databases folder.

The following screenshot shows a file system which contains file, cache files and preferences.

Screenshot of the file system with a few files

Only the application can write into its application directory. It can create additional sub-directories in this application directory. For these sub-directories, the application can grant read or write permissions for other applications.

1.2. Internal vs. external storage

Android has internal storage and external storage. External storage is not private and may not always be available. If for example the Android device is connected with a computer, the computer may mount the external system via USB and that makes this external storage not avaiable for Android applications.

1.3. Application on external storage

As of Android 8 SDK level it is possible to define that the application can or should be placed on external storage. For this set the android:installLocation to preferExternal or auto.

In this case certain application components may be stored on an encrypted external mount point. Database and other private data will still be stored in the internal storage system.

2. Preferences

2.1. Storing key-value pairs

Android supports the usage of the SharedPreferences class for persisting key-value pairs (preferences)of primitive data types in the Android file system.

The definition of these preferences can be done via an XML resource.

The PreferenceManager class provides methods to get access to preferences stored in a certain file. The following code shows how to access preferences from a certain file

# getting preferences from a specified file
SharedPreferences settings = getSharedPreferences("Test", Context.MODE_PRIVATE); 

Tip

Preferences are typically created private and can be accessed via all application components. Sharing data with other application with world readable or writable preference file is rarely used, as the external component would need to know the exact filename and location of the file.

The default preferences are available from any component via the PreferenceManager.getDefaultSharedPreferences(this) method call.

Preference value are accessed via the key and the instance of the SharedPreferences class, as demonstrated in the following listing.

SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getActivity();
String url = settings.getString("url", "n/a"); 

To create or change preferences you have to call the edit() method on the SharedPreferences object. Once you have changed the value you have to call the apply() method to apply your asynchronously to the file system. The usage of the commit() method is discouraged, as it write the changes synchronously to the file system.

Editor edit = preferences.edit();
edit.putString("username", "new_value_for_user");
edit.apply(); 

2.2. Preference Listener

You can listen to changes in the preferences via the registerOnSharedPreferenceChangeListener() method on SharedPreferences.

SharedPreferences prefs = 
    PreferenceManager.getDefaultSharedPreferences(this);

// Instance field for listener
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // Your Implementation
  }
};

prefs.registerOnSharedPreferenceChangeListener(listener); 

One watch out is that SharedPreferences keeps listeners in a WeakHashMap hence listener may be recycled if your code does not hold a reference to it.

3. Tutorial: Prerequirements

The following tutorial is based on the "de.vogella.android.socialapp" example from Android action bar tutorial.

4. Exercise: Prerequisites

The following exercise assumes that you have created an Android project called com.example.android.rssfeed with one entry called Settings in the action bar.

5. Exercise: Preference setting for the RSSfeed

5.1. Create preference file

Create an Android XML resource called mypreferences.xml of the PreferenceScreen type.

How to create an XML file for storing preference value definitions

After creation the correct Android editor should open the file. If not select the file, right-click on it and select Open withAndroid XML Resource Editor.

Press the Add button and add one preferences of type EditTextPreferences. It should be called RSS feed URL and should use the url key.

Adding a category to the preference XML file

Adding a EditTextPreferences preference field

Tip

You can also enter values for other properties of EditTextField, e.g. the inputMethod.

5.2. Create settings activity

Create the class SettingsActivity which extends PreferenceActivity. This activity loads the preference file and allows the user to change the values.

package com.example.android.rssfeed;

import android.os.Bundle;
import android.preference.PreferenceActivity;

public class SettingsActivity extends PreferenceActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.mypreferences);
  }
} 

Register this class as an activity in your AndroidManifest.xml file.

5.3. Connect your settings activity

Open the preference activity via the onOptionsItemSelected() method as demonstrated in the following listing.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
  MenuInflater inflater = getMenuInflater();
  inflater.inflate(R.menu.mainmenu, menu);
  return true;
}

// This method is called once the menu is selected
@Override
public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  case R.id.preferences:
    // Launch settings activity
    Intent i = new Intent(this, SettingsActivity.class);
    startActivity(i);
    break;
    // more code...
  }
  return true;
} 

5.4. Use the preference value to load the RSS feed

To make use of our new preference activity and the preference values we adjust the MyListFragment class. The following code snippet demonstrates how you can access the preference value in a method which triggers an AsyncTask to load the feed from the Internet.

public void updateListContent() {
   if (parseTask == null) {
       parseTask = new ParseTask();
       parseTask.setFragment(this);
       SharedPreferences settings PreferenceManager.getDefaultSharedPreferences(getActivity());
       String url = settings.getString("url", "http://www.vogella.com/article.rss");
       parseTask.execute(url);
   }
} 

5.5. Validate

Run your application. Select from your action bar the Settings action. You should be able to enter an URL. If you press the back button and press the refresh button, ensure that the value of the url preference is used in your activity.

5.6. Optional: Show the current value in the settings

The following code snippet demonstrates how to show the current value in the preference screen.

package com.example.android.rssfeed;

import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;

public class SettingsActivity extends PreferenceActivity implements
    OnSharedPreferenceChangeListener {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.mypreferences);
    
    // show the current value in the settings screen
    for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
      initSummary(getPreferenceScreen().getPreference(i));
    }
  }

  @Override
  protected void onResume() {
    super.onResume();
    getPreferenceScreen().getSharedPreferences()
        .registerOnSharedPreferenceChangeListener(this);
  }

  @Override
  protected void onPause() {
    super.onPause();
    getPreferenceScreen().getSharedPreferences()
        .unregisterOnSharedPreferenceChangeListener(this);
   }

  @Override
  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
      String key) {
    updatePreferences(findPreference(key));
  }

  private void initSummary(Preference p) {
    if (p instanceof PreferenceCategory) {
      PreferenceCategory cat = (PreferenceCategory) p;
      for (int i = 0; i < cat.getPreferenceCount(); i++) {
        initSummary(cat.getPreference(i));
      }
    } else {
      updatePreferences(p);
    }
  }

  private void updatePreferences(Preference p) {
    if (p instanceof EditTextPreference) {
      EditTextPreference editTextPref = (EditTextPreference) p;
      p.setSummary(editTextPref.getText());
    }
  }
} 

Showing the current value in the preferences

6. Android File API

6.1. Using the file API

Access to the file system is performed via the standard java.io classes.

Android provides also helper classes for creating and accessing new files and directories. For example the getDir(String, int) method would create or access a directory. The openFileInput(String s) method would open a file for input and openFileOutput(String s, Context.MODE_PRIVATE) would create a file.

Android supports world readable or writable files, but it is considered good practice to keep files private to the application and use content provider if you want to share data with other applications.

The following example shows the API usage.

public class Util {
  public static void writeConfiguration(Context ctx) {
    BufferedWriter writer = null;
    try {
      FileOutputStream openFileOutput = ctx.openFileOutput("config.txt", Context.MODE_PRIVATE);
      openFileOutput.write("This is a test1.".getBytes());
      openFileOutput.write("This is a test2.".getBytes());
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      if (writer != null) {
        try {
          writer.close();
        } catch (IOException e) {
          e.printStackTrace();
        }

      }
    }
  }
} 

public void readFileFromInternalStorage(String fileName) {
  String eol = System.getProperty("line.separator");
  BufferedReader input = null;
  try {
    input = new BufferedReader(new InputStreamReader(openFileInput(fileName)));
    String line;
    StringBuffer buffer = new StringBuffer();
    while ((line = input.readLine()) != null) {
    buffer.append(line + eol);
    }
  } catch (Exception e) {
     e.printStackTrace();
  } finally {
  if (input != null) {
    try {
    input.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
    }
  }
} 

6.2. External storage

Android supports also access to an external storage system e.g. the SD card. All files and directories on the external storage system are readable for all applications with the correct permission.

To read from external storage your application need to have the android.permission.READ_EXTERNAL_STORAGE permission.

To write to the external storage system your application needs the android.permission.WRITE_EXTERNAL_STORAGE permission. You get the path to the external storage system via the Environment.getExternalStorageDirectory() method.

Via the following method call you can check the state of the external storage system. If the Android device is connected via USB to a computer, a SD card which might be used for the external storage system is not available.

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 

The following shows an example for reading from the external storage system.

private void readFileFromSDCard() {
  File directory = Environment.getExternalStorageDirectory();
  // assumes that a file article.rss is available on the SD card
  File file = new File(directory + "/article.rss");
  if (!file.exists()) {
    throw new RuntimeException("File not found");
  }
  Log.e("Testing", "Starting to read");
  BufferedReader reader = null;
  try {
    reader = new BufferedReader(new FileReader(file));
    StringBuilder builder = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
      builder.append(line);
    }
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    if (reader != null) {
      try {
        reader.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
} 

7. Support this website

This tutorial is Open Content under the CC BY-NC-SA 3.0 DE license. Source code in this tutorial is distributed under the Eclipse Public License. See the vogella License page for details on the terms of reuse.

Writing and updating these tutorials is a lot of work. If this free community service was helpful, you can support the cause by giving a tip as well as reporting typos and factual errors.

7.1. Thank you

Please consider a contribution if this article helped you. It will help to maintain our content and our Open Source activities.

7.2. Questions and Discussion

If you find errors in this tutorial, please notify me (see the top of the page). Please note that due to the high volume of feedback I receive, I cannot answer questions to your implementation. Ensure you have read the vogella FAQ as I don't respond to questions already answered there.

8. Links and Literature

8.1. Source Code

Source Code of Examples

8.3. vogella Resources

vogella Training Android and Eclipse Training from the vogella team

Android Tutorial Introduction to Android Programming

GWT Tutorial Program in Java, compile to JavaScript and HTML

Eclipse RCP Tutorial Create native applications in Java

JUnit Tutorial Test your application

Git Tutorial Put all your files in a distributed version control system