MobileAndroidStoring App-Related Data in Your Android Apps

Storing App-Related Data in Your Android Apps

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Storing some specific info about your app will make it friendlier to use and your user’s life a lot easier. It can be as simple as recording a value for a game level so that you can come back to it next time. And, it can also be as complicated as you want to gather some aggregated knowledge from the info your app has collected so far; for example, who is the best gamer in July this year and what is the score? What are the best, worst, and average scores since the game has been played and when?

Technically, the methods offered by Android will allow you to do all the above in one way or another. You will need to decide which method to use based on the amount of data to store, questions to answer, ability to share, and so forth. We are going to cover these basic ways for data to be stored in simple key-value pairs, files, on internal and external storage, and databases through SQLite.

Saving Data in Simple Key-Value Pairs

If there are only some independent values (for example, perhaps less than 10K) to save the settings or states for the app, you can use android.content.SharedPreferences to record the key-value pairs in a way similar to an associate array. In saveData(), you can see all you need is a key and its value supplied to the class. You also can utilize the optional default value to make sure you have valid data saved or loaded. Usually, loading and saving these key-value pairs are done in onResume() and onPause() for the app activity. When the app is uninstalled, all the saved info will be removed as well.

public class SavingKeyVal extends Activity {
   private int Level = 0;
   private int HighScore = 0;
   private int MyScore = 0;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setContentView(R.layout.algs);

      // make up some new values
      Level = 2;
      MyScore = 2015;
      HighScore = 9378;

      // save the result
      saveData();		
      TextView tv = (TextView)findViewById(R.id.tv);
      tv.setText("Saved!");
   }

   private void saveData() {		
      SharedPreferences sp =
         getSharedPreferences("MyPrefs",
         Context.MODE_PRIVATE);
      SharedPreferences.Editor editor = sp.edit();                                                         			
      editor.putInt("level", Level);
      editor.putInt("myscore", MyScore);
      editor.putInt("highscore", HighScore);
      editor.commit();
   }

   private void loadData() {		
      SharedPreferences sp =
         getSharedPreferences("MyPrefs",
         Context.MODE_PRIVATE);		
      Level = sp.getInt("level", Level);
      MyScore = sp.getInt("myscore", MyScore);
      HighScore = sp.getInt("highscore", HighScore);
   }

   @Override
   protected void onPause() {
      super.onPause();
      saveData();
   }

   @Override
   protected void onResume() {
      super.onResume();
      loadData();
   }
}

Saving Data to Files on Internal Storage

If a large amount of non-well-structured data needs to be saved permanently and is used only by the app, you should consider saving data to files on internal storage. Your app always has permission to read and write files in the internal storage folder, and there is no declaration required. In saveData(), the method getFilesDir() is recommended to return the absolute path to the directory on the system. When the app is uninstalled, all the related files on internal storage will be removed as well.

public class SavingFileInternal extends Activity {
   private String filename = "myapp.txt";
   private String test_string = "This is to be saved
      to a file on internal storage";
    
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setContentView(R.layout.fileint);

      // save the result
      saveData(); 	
      TextView tv = (TextView)findViewById(R.id.tvfileint);
      tv.setText("File saved on internal storage!");
   }

   private void saveData() {     
      try {
         File file = new File(getFilesDir(), filename);
         FileOutputStream fos = new FileOutputStream(file);
         fos.write(test_string.getBytes());
         fos.flush();
         fos.close();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   private void loadData() {       
      try {
         File file = new File(getFilesDir(), filename);
         InputStream is = new
            BufferedInputStream(new FileInputStream(file));
         BufferedReader br = new
            BufferedReader(new InputStreamReader(is));
         String input;
         test_string = "";
         while ((input = br.readLine()) != null)
            test_string += input;
         br.close();
         is.close();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   @Override
   protected void onPause() {
      super.onPause();
      saveData();
   }

   @Override
   protected void onResume() {
      super.onResume();
      loadData();
   }
}

Saving Data to Files on External Storage

First off, you need to request the write permission before saving data to the external storage device. The following line must be added to app’s manifest file.

<uses-permission android_name=
   "android.permission.WRITE_EXTERNAL_STORAGE" />

Secondly, you want to make sure the SD card is mounted and available before writing the files. The following code does the check.

private boolean isExternalStorageAvailable() {
   return Environment.MEDIA_MOUNTED.equals
   (Environment.getExternalStorageState());
}

Other than the above, writing a file to external storage is almost identical to the one we presented for internal storage before. The files are world-readable by other apps. When the app is uninstalled, the system removes related files only if their directory is created from getExternalFilesDir(), as in the example.

public class SavingFileExternal extends Activity {
   private String filename = "myapp2.txt";
   private String filepath = "MYAPPSTORAGE";
   private String test_string = "This is to be saved
      to a file on external storage";

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setContentView(R.layout.fileext);

      // save the result
      saveData();     
      TextView tv = (TextView)findViewById(R.id.tvfileext);
      tv.setText("File saved on external storage!");
   }

   private boolean isExternalStorageAvailable() {
      return Environment.MEDIA_MOUNTED.equals
      (Environment.getExternalStorageState());
   }

   private void saveData() {  
      if (!isExternalStorageAvailable())
            return;

      try {
         File file = new
            File(getExternalFilesDir(filepath), filename);
         FileOutputStream fos = new FileOutputStream(file);
         fos.write(test_string.getBytes());
         fos.flush();
         fos.close();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   private void loadData() {
      if (!isExternalStorageAvailable())
         return;

      try {
         File file = new File(getExternalFilesDir(filepath),
            filename);
         InputStream is = new
            BufferedInputStream(new FileInputStream(file));
         BufferedReader br = new
            BufferedReader(new InputStreamReader(is));
         String input;
         test_string = "";
         while ((input = br.readLine()) != null)
            test_string += input;
         br.close();
         is.close();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   @Override
   protected void onPause() {
      super.onPause();
      saveData();
   }

   @Override
   protected void onResume() {
      super.onResume();
      loadData();
   }

Saving Data to Databases by Using SQLite

If you have lots of data and you need to query it in an efficient way, using SQLite provided by the android.database.sqlite package is the simplest way to go. SQLite supports features such as regular SQL queries. It requires limited runtime memory at about 250 KB, making it suitable for mobile devices. Let us create a simple database, GamersDB, with a table gamers that contains gamer’s id, name, and ratings in the example. SQLite app is started by creating a subclass of SQLiteOpenHelper and overriding its methods.

public class MyAppSQLiteHelper extends SQLiteOpenHelper {
   private static final int DATABASE_VERSION = 1;
   private static final String DATABASE_NAME = "GamersDB";
   private static final String TABLE_GAMERS = "gamers";

   public MyAppSQLiteHelper(Context context) {
      super(context, DATABASE_NAME, null, DATABASE_VERSION);  
   }

   @Override
   public void onCreate(SQLiteDatabase db) {
      String CREATE_GAMERS_TABLE = "CREATE TABLE "
         + TABLE_GAMERS + " ( " +
         "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
         "name TEXT, " +
         "ratings INTEGER )";
      db.execSQL(CREATE_GAMERS_TABLE);
   }

   @Override
   public void onUpgrade(SQLiteDatabase db,
      int oldVersion, int newVersion) {
      db.execSQL("DROP TABLE IF EXISTS " + TABLE_GAMERS);
      this.onCreate(db);
   }

   public void addGamer(Gamer gamer){
      SQLiteDatabase db = this.getWritableDatabase();

      ContentValues values = new ContentValues();
      values.put("name", gamer.name);  
      values.put("ratings", gamer.ratings); 

      db.insert(TABLE_GAMERS, null, values);
      db.close(); 
   }

   public Gamer getGamer(int id){
      SQLiteDatabase db = this.getReadableDatabase();

      Cursor cursor = db.query(TABLE_GAMERS,
         new String[]{"id", "name", "ratings"},
         " id = ?", new String[]{ String.valueOf(id) },                       
         null, null, null, null);

      if (cursor != null)
         cursor.moveToFirst();
      else
         return null;

      Gamer gamer = new Gamer();
      gamer.id = Integer.parseInt(cursor.getString(0));
      gamer.name = cursor.getString(1);
      gamer.ratings =
         Integer.parseInt(cursor.getString(2));
        
      return gamer;
   }
}

Once the database helper is set and ready to go, our main acitivty illustrates how to interact with the database in saveData() by adding new entries and loadData() by retrieving a specific entry.

public class SavingSQLite extends Activity {
   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setContentView(R.layout.sqlite);

      saveData();     
      TextView tv = (TextView)findViewById(R.id.tvsqlite);
      tv.setText("Data saved on SQLite database!");
        
      loadData();
   }

   private void saveData() {     
      MyAppSQLiteHelper db = new MyAppSQLiteHelper(this);
        
      db.addGamer(new Gamer("John", 1829)); 
      db.addGamer(new Gamer("Zoe", 2060));
      db.addGamer(new Gamer("David", 2377));
      db.addGamer(new Gamer("Sandy", 1934));
   }

   private void loadData() {       
      MyAppSQLiteHelper db = new MyAppSQLiteHelper(this);
      Gamer gamer = db.getGamer(2);
        
      TextView tv = (TextView)findViewById(R.id.tvsqlite);        
      tv.setText(gamer.toString());
   }

   @Override
   protected void onPause() {
      super.onPause();
      saveData();
   }

   @Override
   protected void onResume() {
      super.onResume();
      loadData();
   }
}

When the app is uninstalled, all the related databases will be removed, but you can keep the data by hacking to duplicate database files to external storage.

Conclusion

Mobile apps are being operated with limited resources; for example, memory, storage, CPU power, and so on. Certainly, we are already witnessing more modern devices that start to match up with real computers in many ways. However, we still need to always keep in mind that making the design effort regarding resources is a great programming practice.

After this tutorial, you can proceed to think about what data you attempt to incorporate into your apps when they are being used by your audience. All these methods are easy to use and implement but they do have various requirements and limitations, so they serve different purposes. Selecting proper approaches can make the apps run more efficiently and be more compatible without much maintenance later. You can download the software code snippets from the references.

References

About the Author

Chunyen Liu has been a software professional in the USA and Taiwan. He is a published author of 30+ articles and 100+ tiny apps, software patentee, technical reviewer, and programming contest winner by ACM/IBM/SUN. He holds advanced Computer Science degrees, trained in 20+ graduate-level courses. On the non-technical side, he is a certified coach, certified umpire, and rated player of USA Table Tennis, having won categorized events at State Championships and US Open.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories