Data Storage


Published on

Data Storage

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Data Storage

  1. 1. Persistent Data StorageAndroid allows applications to permanently store data on the mobile device for later use and protects it fromaccidental or malicious access by other programs. Saving and loading data is an essential requirement for mostapplications. At a minimum, activities should save their UI state each time they move out of the foreground.This ensures that the same UI state is presented when it’s next seen, even if the process has been killed andrestarted before that happens. It’s also likely that you’ll need to save preferences, to let users customize theapplication, and persist data entered or recorded. Just as important is the ability to load data from files,databases, or Content Providers.An application can store data using several techniques depending on the size of the data, its structure, itslifetime, and whether it will be shared with other programs. 1. Preferences are a simple, lightweight key/value pair mechanism for saving primitive application data, most commonly UI state, user preferences, or application settings. 2. Android also provides access to the local file system, both through specialized methods and the normal Java.IO classes. 3. For a more robust persistence layer, Android provides the SQLite database library. The SQLite relational database offers a powerful native SQL database over which you have total control. 4. Content Providers offer a generic interface to any data source. They effectively decouple the underlying data storage technique from the application layer. They let you expose a well-defined interface for using and sharing private data.By default, access to all files, databases, and preferences is restricted to the application that created them.Content Providers offer a managed way for your applications to share private data with other applications. As aresult, your applications can use the Content Providers offered by others, including native providers.There are two lightweight techniques for saving simple application data for Android applications: (1) SharedPreferences and (2) a pair of event handlers used for saving the details of an Activity instance. Both mechanismsuse a name-value pair mechanism to store simple primitive values. 1. Shared Preferences support the primitive types boolean, float, long, int, and String making them an ideal way to quickly store default values, class instance variables, the current UI state, and user preferences. They are most commonly used to persist data across user sessions and to share settings between application components. 2. Alternatively, Activities offer the onSaveInstanceState handler. It’s designed specifically to persist the UI state when the Activity becomes eligible for termination by a resource-hungry run time. The handler works like the Shared Preference mechanism. It offers a Bundle parameter that represents a key/ value map of primitive types that can be used to save the Activity’s instance values. This Bundle is then made available as a parameter passed in to the onCreate() and onRestoreInstanceState() method handlers. This UI state Bundle is used to record the values needed for an Activity to provide an identical UI following unexpected restarts.1. Shared PreferencesShared preferences are preferences that are shared between all activities of an application and can be managedwith the help of the getSharedPreferences() method of the Context class. This method takes twoarguments, the name of the set of shared preferences and the operating mode. MODE_PRIVATE is the defaultoperating mode and means the created set of preferences can be accessed only by the application that created it.Basically, the shared preferences are shared across the application’s components but aren’t available to otherapplications. The getSharedPreferences() method returns a SharedPreferences object that holdsthe contents of the specified preference set through which you can retrieve or modify the preference values.
  2. 2. public static final String PREF_SET_NAME = "PrefSet"; SharedPreferences preferences = getSharedPreferences(PREF_SET_NAME,MODE_PRIVATE);Remark: Note that if several application components call the getSharedPreferences() method with thesame preference set name, then the same SharedPreferences object is returned to all callers (meaning theywill see each others edits as soon as they are made).To edit/modify a shared preference, use the SharedPreferences.Editor class. Get the Editor objectby calling edit() on the SharedPreferences object you want to change and then use mutators/settersput<type> to save value of specific types. To save edits, call commit() on the Editor, as illustrated in thefollowing generic example. SharedPreferences.Editor editor = preferences.edit(); editor.putInt("key1", int_value); editor.putString("key2", string_value); editor.commit();To access/retrieve saved shared preferences use the type-safe get<type> methods. Each getter takes a key and adefault value (used when no value is available for that key). int value1 = preferences.getInt("key1", 0); String value2 = preferences.getString("key2", "abc");If a preference does not need to be shared across multiple components of an application, but is needed only byone Activity for example, then you can use the getPreferences() method of the Activity class (insteadof the getSharedPreferences() method of the Context class). The getPreferences() methodinvokes the getSharedPreferences() method with the name of the activity class as the name for that setof preferences. The code to modify and access these preferences (called activity preferences) is the same as forshared preferences.1.1. Implementing the Continue Game option using PreferencesWe will illustrate the use of activity preferences by implementing the option to continue an old game in ourSudoku application. A player can decide to quit playing a Sudoku game at any point in time. To allow the playerto come back later and continue where they left off, we first need to save the current state of the puzzle (i.e., thenumbers in each of the 9x9 = 81 tiles) whenever the game is paused. Therefore, in the onPause() method ofthe Game activity, we first concatenate the numbers from the 81 tiles into a string, using methodtoPuzzleString() and then store the value of this string paired with a key into the shared preferences. Inour code below we use the constant "puzzle" as the key. private static final String PREF_PUZZLE = "puzzle" ; @Override protected void onPause() { super.onPause(); // Save the current puzzle String s = toPuzzleString(puzzle); getPreferences(MODE_PRIVATE).edit().putString(PREF_PUZZLE, s).commit(); }
  3. 3. Now that the puzzle is saved, in order to read it when needed, we will just modify method getPuzzle(),which loads a puzzle (according to the difficulty level selected by the player). All we need to do is use a flag,for example -1, to indicate that the puzzle saved in the preferences should be loaded (instead of starting a newpuzzle of a certain difficulty level). /** Given a difficulty level, come up with a new puzzle */ private int[] getPuzzle(int diff) { String puz; switch (diff) { case -1: puz = getPreferences(MODE_PRIVATE).getString(PREF_PUZZLE, easyPuzzle); break; case 0: puz = hardPuzzle; break; case 1: puz = mediumPuzzle; break; case 2: default: puz = easyPuzzle; break; } return fromPuzzleString(puz); }Now when player chooses the option to continue an old game, we can call the same method used for starting anew game, startGame(), but passing it -1 instead of a difficulty level (0, 1 or 2) to indicate that the puzzleto be loaded is the one stored in preferences. public class Sudoku extends Activity implements OnClickListener { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Set up click listeners for all the buttons View continueButton = this.findViewById(; continueButton.setOnClickListener(this); View newButton = this.findViewById(; newButton.setOnClickListener(this); View aboutButton = this.findViewById(; aboutButton.setOnClickListener(this); View exitButton = this.findViewById(; exitButton.setOnClickListener(this); } public void onClick(View v) { switch (v.getId()) { case startGame(-1); break; case Intent i = new Intent(this, About.class); startActivity(i); break; case openNewGameDialog();
  4. 4. break; case finish(); break; } } /** Start a new game with the given difficulty level */ private void startGame(int i) { Intent intent = new Intent(Sudoku.this, Game.class); intent.putExtra(Game.KEY_DIFFICULTY, i); startActivity(intent); } }2. Storing the State of an Activity InstanceTo save Activity instance variables, Android offers a specialized alternative to preferences. By overriding anActivity’s onSaveInstanceState event handler, you can use its Bundle parameter to save instancevalues. Values are stored/retrieved to/from this Bundle object using the same get and put methods as shownfor preferences. This handler will be triggered whenever an Activity completes its active life cycle, but onlywhen it’s not being explicitly finished. As a result, it’s used to ensure a consistent Activity state between activelife cycles of a single user session. The saved Bundle is passed in to the onRestoreInstanceState()and onCreate() methods if the application is forced to restart during a session.2.1. Remembering the Selected Tile when Screen Orientation ChangesWe will illustrate how to use the onSaveInstanceState event handler to remember the current position ofthe selected tile when the player changes the screen orientation while Sudoku is running. In this case, we need tostore the values of data fields selX and selY (indicating the selected tile) from the PuzzleView class. Thus,as for shared preferences we first define two constants "selX" and "selY" as keys for the values to be stored.Note that normally, Android views save their state automatically, but since we made our own view, we don’t getthat for free.Notice that the PuzzleView class is a View (not an Activity). The View class also has aonSaveInstanceState() method that is invoked automatically by theActivity.onSaveInstanceState() method for every hosted view that has an ID. Normally, this IDwould come from XML, but since PuzzleView was created in code, we need to set it ourselves. Thus, wemake up an arbitrary number (any value will work as long as it is positive) and then use the setId() methodto assign it to our view in the constructor.In the onSaveInstanceState() method we call the superclass to get its state, and then save it plus theadditional value-key pairs in a Bundle object. Later, onRestoreInstanceState() will be called to readthe information we saved. We get our own x and y positions from the Bundle, and then we call the superclass tolet it get whatever it needs. public class PuzzleView extends View { private int selX; // X index of selection private int selY; // Y index of selection … private static final String SELX = "selX"; private static final String SELY = "selY"; private static final String VIEW_STATE = "viewState"; private static final int ID = 42;
  5. 5. public PuzzleView(Context context) { ... setId(ID); } @Override protected Parcelable onSaveInstanceState() { Parcelable p = super.onSaveInstanceState(); Bundle bundle = new Bundle(); bundle.putInt(SELX, selX); bundle.putInt(SELY, selY); bundle.putParcelable(VIEW_STATE, p); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { Bundle bundle = (Bundle) state; select(bundle.getInt(SELX), bundle.getInt(SELY)); super.onRestoreInstanceState(bundle.getParcelable(VIEW_STATE)); return; } }3. Describing Application Preferences/Settings using XML filesBeginning with the 0.9 SDK, Android has introduced a framework for managing applicationpreferences/settings. This framework does not change anything shown above. Instead, the framework is morefor presenting a consistent set of preference-setting options for users, so different applications do not have to"reinvent the wheel". Basically, the framework allows you to describe an applications preferences/settings in anXML file stored in your projects res/xml/ directory. Given that, Android can present a pleasant user UI formanipulating those preferences, which are then stored in a SharedPreferences object, which can beobtained through the getDefaultSharedPreferences() method of the PreferencesManagerclass. This method is similar to getPreferences() and getSharedPreferences(), in the sense thatit returns a SharedPreferences object which offers a series of type-safe getters to access preferencevalues.We illustrate an XML file containing application preferences with our Sudoku example. In Sudoku the user hasthe option to have music playing while he is playing the game. This application setting is described in thefollowing XML file.res/xml/settings.xml <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android=""> <CheckBoxPreference android:key="music" android:title="@string/music_title" android:summary="@string/music_summary" android:defaultValue="true" /> </PreferenceScreen>Note that music_title and music_summary are defined in the res/values/strings.xml ... <string name="music_title">Music</string>
  6. 6. <string name="music_summary">Play background music</string> ...The root of the preference XML document is a PreferenceScreen element. Inside aPreferenceScreen element you can have preference definitions. These are subclasses of Preference,such as CheckBoxPreference as shown above, which allows you to check a checkbox.Once you have set up your preference XML, you can use a nearly-built-in activity for allowing your users to settheir preferences. The activity is "nearly-built-in" because you need to subclass it to point it to your preferenceXML, plus hook it into the rest of your application. For our Sudoku example, let’s call this activity Settings. public class Settings extends PreferenceActivity { // Options names and default values private static final String OPT_MUSIC = "music"; private static final boolean OPT_MUSIC_DEF = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings); } /** Get the current value of the music option */ public static boolean getMusic(Context context) { return PreferenceManager.getDefaultSharedPreferences(context) .getBoolean(OPT_MUSIC, OPT_MUSIC_DEF); } }As you can see, all you need to do is extend the PreferenceActivity class and, in the onCreate()method, call the addPreferencesFromResource() method specifying the XML resource containingyour preferences (in our example, R.xml.settings).Note: Do not forget to add this activity to your AndroidManifest.xml file.Now we have to link this activity with the rest of the Sudoku application. Specifically, we have to invoke thisactivity (through an Intent) when the user presses the Menu button. public class Sudoku extends Activity implements OnClickListener { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case startActivity(new Intent(this, Settings.class)); return true; } return false; }
  7. 7. }If you have a lot of preferences for users to set, having them all in one big list may become troublesome.Androids preference framework gives you a few ways to impose a bit of structure on your bag of preferences,including categories and screens. Categories are added via a PreferenceCategory element in yourpreference XML and are used to group together related preferences. Rather than have your preferences all aschildren of the root PreferenceScreen, you can put a few PreferenceCategory elements in thePreferenceScreen, and then put your preferences in their appropriate categories. Visually, this adds adivider with the category title between groups of preferences. If you have lots and lots of preferences – morethan is convenient for users to scroll through – you can also put them on separate "screens" by introducing thePreferenceScreen element. Any children of PreferenceScreen go on their own screen. If you nestPreferenceScreens, the parent screen displays the screen as a placeholder entry – tapping that entry bringsup the child screen. For example, here is a preference XML file that contains both PreferenceCategoryand nested PreferenceScreen elements. <PreferenceScreen xmlns:android=""> <PreferenceCategory android:title="Simple Preferences"> <CheckBoxPreference android:key="@string/checkbox" android:title="Checkbox Preference" android:summary="Check it on, check it off"
  8. 8. /> <RingtonePreference android:key="@string/ringtone" android:title="Ringtone Preference" android:showDefault="true" android:showSilent="true" android:summary="Pick a tone, any tone" /> </PreferenceCategory> <PreferenceCategory android:title="Detail Screens"> <PreferenceScreen android:key="detail" android:title="Detail Screen" android:summary="Additional preferences held in another page"> <CheckBoxPreference android:key="@string/checkbox2" android:title="Another Checkbox" android:summary="On. Off. It really doesnt matter." /> </PreferenceScreen> </PreferenceCategory> </PreferenceScreen>If you tap on the Detail Screen entry, you are taken to the child preference screen: