Logo Icon Logo
A Crowd-sourced Cookbook on Writing Great Android® Apps
GitHub logo Twitter logo OReilly Book Cover Art

Backing Up Android Application Data

Author: Pratik Rupwal
Published? true
FormatLanguage: WikiFormat

Problem:

If a user performs a factory reset or converts to a new Android-powered device, the application loses stored data or application settings.

Solution:

Android's Backup Manager helps to automatically restore your backup data or application settings when the application is re-installed.

Discussion:

Android's Backup mechanism basically operated in two modes, backup and restore. During a backup operation, Android's Backup Manager (BackupManager class) queries your application for backup data, then hands it to a backup transport, which then delivers the data to the cloud storage. During a restore operation, the Backup Manager retrieves the backup data from the backup transport and returns it to your application so your application can restore the data to the device. It's possible for your application to request a restore, which is not necessary as Android automatically performs a restore operation when your application is installed and there exists backup data associated with the user. The primary scenario in which backup data is restored when a user resets their device or upgrades to a new device and their previously installed applications are re-installed.

Below application describes how to implement BackupManager for your application so that you can save the current state of your application.

Basic description of procedure in step-by-step form:

1) Create a Project 'BackupManagerExample' in eclipse.

2) Open and insert the following code in layout/backup_restore.xml:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

    <ScrollView
       android:orientation="vertical"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:layout_weight="1">

        <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">

            <TextView android:text="@string/filling_text"
               android:textSize="20dp"
               android:layout_marginTop="20dp"
               android:layout_marginBottom="10dp"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"/>

            <RadioGroup android:id="@+id/filling_group"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:layout_marginLeft="20dp"
               android:orientation="vertical">

                <RadioButton android:id="@+id/bacon"
                   android:text="@string/bacon_label"/>
                <RadioButton android:id="@+id/pastrami"
                   android:text="@string/pastrami_label"/>
                <RadioButton android:id="@+id/hummus"
                   android:text="@string/hummus_label"/>

            </RadioGroup>

            <TextView android:text="@string/extras_text"
               android:textSize="20dp"
               android:layout_marginTop="20dp"
               android:layout_marginBottom="10dp"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"/>

            <CheckBox android:id="@+id/mayo"
               android:text="@string/mayo_text"
               android:layout_marginLeft="20dp"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"/>

            <CheckBox android:id="@+id/tomato"
               android:text="@string/tomato_text"
               android:layout_marginLeft="20dp"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"/>

        </LinearLayout>

    </ScrollView>

</LinearLayout>

3) Open values/string.xml and insert the following code in it,

<resources>
  <string name="hello">Hello World, BackupManager!</string>
  <string name="app_name">BackupManager</string>
  <string name="filling_text">Choose Settings for your application:</string>
  <string name="bacon_label">Sound On</string>
  <string name="pastrami_label">Vibration On</string>
  <string name="hummus_label">Backlight On</string>
  <string name="extras_text">Extras:</string>
  <string name="mayo_text">Use Orientation?</string>
  <string name="tomato_text">Use Camera?</string>
</resources>

4)Your manifest file will look like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.sym.backupmanager"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="9" />

    <application android:label="Backup/Restore" android:icon="@drawable/icon"
        android:backupAgent="ExampleAgent"> <!-- Here you specify the backup agent-->

        <!--Some backup transports may require API keys or other metadata-->
        <meta-data android:name="com.google.android.backup.api_key"
                android:value="INSERT YOUR API KEY HERE" />

        <activity android:name=".BackupManagerExample">
                <intent-filter>
                        <action android:name="android.intent.action.MAIN" />
                        <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
        </activity> </application>

</manifest>

5) The following code completes the implementation of the BackupManager for your application.

package com.sym.backupmanager;

import android.app.Activity;
import android.app.backup.BackupManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.RadioGroup;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

public class BackupManagerExample extends Activity {
        static final String TAG = "BRActivity";

    static final Object[] sDataLock = new Object[0];

    static final String DATA_FILE_NAME = "saved_data";

    RadioGroup mFillingGroup;
    CheckBox mAddMayoCheckbox;
    CheckBox mAddTomatoCheckbox;

    File mDataFile;

    BackupManager mBackupManager;

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

        setContentView(R.layout.backup_restore);

        mFillingGroup = (RadioGroup) findViewById(R.id.filling_group);
        mAddMayoCheckbox = (CheckBox) findViewById(R.id.mayo);
        mAddTomatoCheckbox = (CheckBox) findViewById(R.id.tomato);

         mDataFile = new File(getFilesDir(), BackupManagerExample.DATA_FILE_NAME);

         mBackupManager = new BackupManager(this);

        populateUI();
    }

    void populateUI() {
        RandomAccessFile file;

        int whichFilling = R.id.pastrami;
        boolean addMayo = false;
        boolean addTomato = false;

        synchronized (BackupManagerExample.sDataLock) {
            boolean exists = mDataFile.exists();
            try {
                file = new RandomAccessFile(mDataFile, "rw");
                if (exists) {
                    Log.v(TAG, "datafile exists");
                    whichFilling = file.readInt();
                    addMayo = file.readBoolean();
                    addTomato = file.readBoolean();
                    Log.v(TAG, "  mayo=" + addMayo
                            + " tomato=" + addTomato
                            + " filling=" + whichFilling);
                } else {
                    Log.v(TAG, "creating default datafile");
                    writeDataToFileLocked(file,
                            addMayo, addTomato, whichFilling);

                    mBackupManager.dataChanged();
                }
            } catch (IOException ioe) {

            }
        }

        mFillingGroup.check(whichFilling);
        mAddMayoCheckbox.setChecked(addMayo);
        mAddTomatoCheckbox.setChecked(addTomato);

        mFillingGroup.setOnCheckedChangeListener(
                new RadioGroup.OnCheckedChangeListener() {
                    public void onCheckedChanged(RadioGroup group,
                            int checkedId) {
                        Log.v(TAG, "New radio item selected: " + checkedId);
                        recordNewUIState();
                    }
                });

        CompoundButton.OnCheckedChangeListener checkListener
                = new CompoundButton.OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView,
                    boolean isChecked) {
                Log.v(TAG, "Checkbox toggled: " + buttonView);
                recordNewUIState();
            }
        };
        mAddMayoCheckbox.setOnCheckedChangeListener(checkListener);
        mAddTomatoCheckbox.setOnCheckedChangeListener(checkListener);
    }

    void writeDataToFileLocked(RandomAccessFile file,
            boolean addMayo, boolean addTomato, int whichFilling)
        throws IOException {
            file.setLength(0L);
            file.writeInt(whichFilling);
            file.writeBoolean(addMayo);
            file.writeBoolean(addTomato);
            Log.v(TAG, "NEW STATE: mayo=" + addMayo
                    + " tomato=" + addTomato
                    + " filling=" + whichFilling);
    }

    void recordNewUIState() {
        boolean addMayo = mAddMayoCheckbox.isChecked();
        boolean addTomato = mAddTomatoCheckbox.isChecked();
        int whichFilling = mFillingGroup.getCheckedRadioButtonId();
        try {
            synchronized (BackupManagerExample.sDataLock) {
                RandomAccessFile file = new RandomAccessFile(mDataFile, "rw");
                writeDataToFileLocked(file, addMayo, addTomato, whichFilling);
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to record new UI state");
        }

        mBackupManager.dataChanged();
    }
}

Data backup is not guaranteed to be available on all Android-powered devices. However, your application is not adversely affected in the event that a device does not provide a backup transport. If you believe that users will benefit from data backup in your application, then you can implement it as described in this document, test it, then publish your application without any concern about which devices actually perform backup. When your application runs on a device that does not provide a backup transport, your application operates normally, but will not receive callbacks from the Backup Manager to backup data.

Although you cannot know what the current transport is, you are always assured that your backup data cannot be read by other applications on the device. Only the Backup Manager and backup transport have access to the data you provide during a backup operation.

Caution: Because the cloud storage and transport service can differ from device to device, Android makes no guarantees about the security of your data while using backup. You should always be cautious about using backup to store sensitive data, such as usernames and passwords.

See Also:

Testing Your Backup Agent:

Once you've implemented your backup agent, you can test the backup and restore functionality with the following procedure, using bmgr.

   1. Install your application on a suitable Android system image
       If using the emulator, create and use an AVD with Android 2.2 (API Level 8).
       If using a device, the device must be running Android 2.2 or greater and have Android Market built in.
   2. Ensure that backup is enabled
       If using the emulator, you can enable backup with the following command from your SDK tools/ path:
       adb shell bmgr enable true
       If using a device, open the system Settings, select Privacy, then enable Back up my data and Automatic restore. 
   3. Open your application and initialize some data
   If you've properly implemented backup in your application, then it should request a backup each time the data changes. For example,

each time the user changes some data, your app should call dataChanged(), which adds a backup request to the Backup Manager queue. For testing purposes, you can also make a request with the following bmgr command:

   adb shell bmgr backup your.package.name
   4.Initiate a backup operation:
   adb shell bmgr run
   This forces the Backup Manager to perform all backup requests that are in its queue.
   
   5.Uninstall your application:
   adb uninstall your.package.name
   6.Re-install your application.

If your backup agent is successful, all the data you initialized in step 4 is restored.

No records found.