Android's
backup
service allows you to copy your persistent application data to remote "cloud" storage, in order to provide a restore point for the application data and settings. If a user performs a factory reset or converts to a new Android-powered device, the system automatically restores your backup data when the application is re-installed. This way, your users don't need to reproduce their previous data or application settings. This process is completely transparent to the user and does not affect the functionality or user experience in your application.
During a backup operation (which your application can request), Android's Backup Manager (
BackupManager
) 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, but that shouldn't be necessary—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 is when a user resets their device or upgrades to a new device and their previously installed applications are re-installed.
Note: The backup service is not designed for synchronizing application data with other clients or saving data that you'd like to access during the normal application lifecycle. You cannot read or write backup data on demand and cannot access it in any way other than through the APIs provided by the Backup Manager.
The backup transport is the client-side component of Android's backup framework, which is customizable by the device manufacturer and service provider. The backup transport may differ from device to device and which backup transport is available on any given device is transparent to your application. The Backup Manager APIs isolate your application from the actual backup transport available on a given device—your application communicates with the Backup Manager through a fixed set of APIs, regardless of the underlying transport.
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.
The Basics
To backup your application data, you need to implement a backup agent. Your backup agent is called by the Backup Manager to provide the data you want to back up. It is also called to restore your backup data when the application is re-installed. The Backup Manager handles all your data transactions with the cloud storage (using the backup transport) and your backup agent handles all your data transactions on the device.
To implement a backup agent, you must:
- Declare your backup agent in your manifest file with the
android:backupAgent
attribute.
- Register your application with a backup service. Google offers Android Backup Service as a backup service for most Android-powered devices, which requires that you register your application in order for it to work. Any other backup services available might also require you to register in order to store your data on their servers.
- Define a backup agent by either:
- Extending BackupAgent
The
BackupAgent
class provides the central interface with which your application communicates with the Backup Manager. If you extend this class directly, you must override
onBackup()
and
onRestore()
to handle the backup and restore operations for your data.
Or
- Extending BackupAgentHelper
The
BackupAgentHelper
class provides a convenient wrapper around the
BackupAgent
class, which minimizes the amount of code you need to write. In your
BackupAgentHelper
, you must use one or more "helper" objects, which automatically backup and restore certain types of data, so that you do not need to implement
onBackup()
and
onRestore()
.
Declaring the Backup Agent in Your Manifest
This is the easiest step, so once you've decided on the class name for your backup agent, declare it in your manifest with the
android:backupAgent
attribute in the
<application>
tag.
For example:
<manifest ... >
...
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
<activity ... >
...
</activity>
</application>
</manifest>
Another attribute you might want to use is
android:restoreAnyVersion
. This attribute takes a boolean value to indicate whether you want to restore the application data regardless of the current application version compared to the version that produced the backup data. (The default value is "
false
".) See
Checking the Restore Data Version for more information.
Note: The backup service and the APIs you must use are available only on devices running API Level 8 (Android 2.2) or greater, so you should also set your
android:minSdkVersion
attribute to "8".
Registering for Android Backup Service
Google provides a backup transport with
Android Backup Service for most Android-powered devices running Android 2.2 or greater.
In order for your application to perform backup using Android Backup Service, you must register your application with the service to receive a Backup Service Key, then declare the Backup Service Key in your Android manifest.
To get your Backup Service Key,
register for Android Backup Service. When you register, you will be provided a Backup Service Key and the appropriate
<meta-data>
XML code for your Android manifest file, which you must include as a child of the
<application>
element. For example:
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" />
</application>
The android:name
must be "com.google.android.backup.api_key"
and the android:value
must be the Backup Service Key received from the Android Backup Service registration.
If you have multiple applications, you must register each one, using the respective package name.
Note: The backup transport provided by Android Backup Service is not guaranteed to be available on all Android-powered devices that support backup. Some devices might support backup using a different transport, some devices might not support backup at all, and there is no way for your application to know what transport is used on the device. However, if you implement backup for your application, you should always include a Backup Service Key for Android Backup Service so your application can perform backup when the device uses the Android Backup Service transport. If the device does not use Android Backup Service, then the <meta-data>
element with the Backup Service Key is ignored.
Extending BackupAgent
Most applications shouldn't need to extend the
BackupAgent
class directly, but should instead
extend BackupAgentHelper to take advantage of the built-in helper classes that automatically backup and restore your files. However, you might want to extend
BackupAgent
directly if you need to:
- Version your data format. For instance, if you anticipate the need to revise the format in which you write your application data, you can build a backup agent to cross-check your application version during a restore operation and perform any necessary compatibility work if the version on the device is different than that of the backup data. For more information, see Checking the Restore Data Version.
- Instead of backing up an entire file, you can specify the portions of data the should be backed up and how each portion is then restored to the device. (This can also help you manage different versions, because you read and write your data as unique entities, rather than complete files.)
- Back up data in a database. If you have an SQLite database that you want to restore when the user re-installs your application, you need to build a custom
BackupAgent
that reads the appropriate data during a backup operation, then create your table and insert the data during a restore operation.
Required Methods
When you create a backup agent by extending
BackupAgent
, you must implement the following callback methods:
onBackup()
- The Backup Manager calls this method after you request a backup. In this method, you read your application data from the device and pass the data you want to back up to the Backup Manager, as described below in Performing backup.
onRestore()
- The Backup Manager calls this method during a restore operation (you can request a restore, but the system automatically performs restore when the user re-installs your application). When it calls this method, the Backup Manager delivers your backup data, which you then restore to the device, as described below in Performing restore.
When it's time to back up your application data, the Backup Manager calls your
onBackup()
method. This is where you must provide your application data to the Backup Manager so it can be saved to cloud storage.
Only the Backup Manager can call your backup agent's
onBackup()
method. Each time that your application data changes and you want to perform a backup, you must request a backup operation by calling
dataChanged()
(see
Requesting Backup for more information). A backup request does not result in an immediate call to your
onBackup()
method. Instead, the Backup Manager waits for an appropriate time, then performs backup for all applications that have requested a backup since the last backup was performed.
Tip: While developing your application, you can initiate an immediate backup operation from the Backup Manager with the
bmgr
tool.
When the Backup Manager calls your
onBackup()
method, it passes three parameters:
oldState
- An open, read-only
ParcelFileDescriptor
pointing to the last backup state provided by your application. This is not the backup data from cloud storage, but a local representation of the data that was backed up the last time onBackup()
was called (as defined by newState
, below, or fromonRestore()
—more about this in the next section). Because onBackup()
does not allow you to read existing backup data in the cloud storage, you can use this local representation to determine whether your data has changed since the last backup.
data
- A
BackupDataOutput
object, which you use to deliver your backup data to the Backup Manager.
newState
- An open, read/write
ParcelFileDescriptor
pointing to a file in which you must write a representation of the data that you delivered to data
(a representation can be as simple as the last-modified timestamp for your file). This object is returned as oldState
the next time the Backup Manager calls your onBackup()
method. If you do not write your backup data to newState
, then oldState
will point to an empty file next time Backup Manager calls onBackup()
.
Using these parameters, you should implement your
onBackup()
method to do the following:
- Check whether your data has changed since the last backup by comparing
oldState
to your current data. How you read data in oldState
depends on how you originally wrote it to newState
(see step 3). The easiest way to record the state of a file is with its last-modified timestamp. For example, here's how you can read and compare a timestamp from oldState
:// Get the oldState input stream
FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());
DataInputStream in = new DataInputStream(instream);
try {
// Get the last modified timestamp from the state file and data file
long stateModified = in.readLong();
long fileModified = mDataFile.lastModified();
if (stateModified != fileModified) {
// The file has been modified, so do a backup
// Or the time on the device changed, so be safe and do a backup
} else {
// Don't back up because the file hasn't changed
return;
}
} catch (IOException e) {
// Unable to read state file... be safe and do a backup
}
If nothing has changed and you don't need to back up, skip to step 3.
- If your data has changed, compared to
oldState
, write the current data to data
to back it up to the cloud storage.
You must write each chunk of data as an "entity" in the
BackupDataOutput
. An entity is a flattened binary data record that is identified by a unique key string. Thus, the data set that you back up is conceptually a set of key-value pairs.
To add an entity to your backup data set, you must:
- Call
writeEntityHeader()
, passing a unique string key for the data you're about to write and the data size.
- Call
writeEntityData()
, passing a byte buffer that contains your data and the number of bytes to write from the buffer (which should match the size passed to writeEntityHeader()
).
For example, the following code flattens some data into a byte stream and writes it into a single entity:
// Create buffer stream and data output stream for our data
ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
DataOutputStream outWriter = new DataOutputStream(bufStream);
// Write structured data
outWriter.writeUTF(mPlayerName);
outWriter.writeInt(mPlayerScore);
// Send the data to the Backup Manager via the BackupDataOutput
byte[] buffer = bufStream.toByteArray();
int len = buffer.length;
data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len);
data.writeEntityData(buffer, len);
Perform this for each piece of data that you want to back up. How you divide your data into entities is up to you (and you might use just one entity).
- Whether or not you perform a backup (in step 2), write a representation of the current data to the
newState
ParcelFileDescriptor
. The Backup Manager retains this object locally as a representation of the data that is currently backed up. It passes this back to you as oldState
the next time it calls onBackup()
so you can determine whether another backup is necessary (as handled in step 1). If you do not write the current data state to this file, then oldState
will be empty during the next callback.
The following example saves a representation of the current data into newState
using the file's last-modified timestamp:
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
long modified = mDataFile.lastModified();
out.writeLong(modified);
Caution: If your application data is saved to a file, make sure that you use synchronized statements while accessing the file so that your backup agent does not read the file while an Activity in your application is also writing the file.
When it's time to restore your application data, the Backup Manager calls your backup agent's
onRestore()
method. When it calls this method, the Backup Manager delivers your backup data so you can restore it onto the device.
Only the Backup Manager can call
onRestore()
, which happens automatically when the system installs your application and finds existing backup data. However, you can request a restore operation for your application by calling
requestRestore()
(see
Requesting restore for more information).
Note: While developing your application, you can also request a restore operation with the
bmgr
tool.
When the Backup Manager calls your
onRestore()
method, it passes three parameters:
data
- A
BackupDataInput
, which allows you to read your backup data.
appVersionCode
- An integer representing the value of your application's
android:versionCode
manifest attribute, as it was when this data was backed up. You can use this to cross-check the current application version and determine if the data format is compatible. For more information about using this to handle different versions of restore data, see the section below about Checking the Restore Data Version.
newState
- An open, read/write
ParcelFileDescriptor
pointing to a file in which you must write the final backup state that was provided with data
. This object is returned as oldState
the next time onBackup()
is called. Recall that you must also write the same newState
object in the onBackup()
callback—also doing it here ensures that the oldState
object given to onBackup()
is valid even the first time onBackup()
is called after the device is restored.
In your implementation of
onRestore()
, you should call
readNextHeader()
on the
data
to iterate through all entities in the data set. For each entity found, do the following:
- Get the entity key with
getKey()
.
- Compare the entity key to a list of known key values that you should have declared as static final strings inside your
BackupAgent
class. When the key matches one of your known key strings, enter into a statement to extract the entity data and save it to the device:
- Get the entity data size with
getDataSize()
and create a byte array of that size.
- Call
readEntityData()
and pass it the byte array, which is where the data will go, and specify the start offset and the size to read.
- Your byte array is now full and you can read the data and write it to the device however you like.
- After you read and write your data back to the device, write the state of your data to the
newState
parameter the same as you do during onBackup()
.
For example, here's how you can restore the data backed up by the example in the previous section:
@Override
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
// There should be only one entity, but the safest
// way to consume it is using a while loop
while (data.readNextHeader()) {
String key = data.getKey();
int dataSize = data.getDataSize();
// If the key is ours (for saving top score). Note this key was used when
// we wrote the backup entity header
if (TOPSCORE_BACKUP_KEY.equals(key)) {
// Create an input stream for the BackupDataInput
byte[] dataBuf = new byte[dataSize];
data.readEntityData(dataBuf, 0, dataSize);
ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
DataInputStream in = new DataInputStream(baStream);
// Read the player name and score from the backup data
mPlayerName = in.readUTF();
mPlayerScore = in.readInt();
// Record the score on the device (to a file or something)
recordScore(mPlayerName, mPlayerScore);
} else {
// We don't know this entity key. Skip it. (Shouldn't happen.)
data.skipEntityData();
}
}
// Finally, write to the state blob (newState) that describes the restored data
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
out.writeUTF(mPlayerName);
out.writeInt(mPlayerScore);
}
In this example, the
appVersionCode
parameter passed to
onRestore()
is not used. However, you might want to use it if you've chosen to perform backup when the user's version of the application has actually moved backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see the section about
Checking the Restore Data Version.
Extending BackupAgentHelper
Your implementation of
BackupAgentHelper
must use one or more backup helpers. A backup helper is a specialized component that
BackupAgentHelper
summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
- Instantiate in instance of the desired helper class. In the class constructor, you must specify the appropriate file(s) you want to backup.
- Call
addHelper()
to add the helper to your BackupAgentHelper
.
The following sections describe how to create a backup agent using each of the available helpers.
Backing up SharedPreferences
public class MyPrefsBackupAgent extends BackupAgentHelper {
// The name of the SharedPreferences file
static final String PREFS = "user_preferences";
// A key to uniquely identify the set of backup data
static final String PREFS_BACKUP_KEY = "prefs";
// Allocate a helper and add it to the backup agent
@Override
public void onCreate() {
SharedPreferencesBackupHelper helper =
new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
Note: SharedPreferences
are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.
Backing up other files
For example, to backup two files named "scores" and "stats," a backup agent using
BackupAgentHelper
looks like this:
public class MyFileBackupAgent extends BackupAgentHelper {
// The name of the file
static final String TOP_SCORES = "scores";
static final String PLAYER_STATS = "stats";
// A key to uniquely identify the set of backup data
static final String FILES_BACKUP_KEY = "myfiles";
// Allocate a helper and add it to the backup agent
@Override
public void onCreate() {
FileBackupHelper helper = new FileBackupHelper(this,
TOP_SCORES, PLAYER_STATS);
addHelper(FILES_BACKUP_KEY, helper);
}
}
However, reading and writing to files on internal storage is not threadsafe. To ensure that your backup agent does not read or write your files at the same time as your activities, you must use synchronized statements each time you perform a read or write. For example, in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:
// Object for intrinsic lock
static final Object sDataLock = new Object();
Then create a synchronized statement with this lock each time you read or write the files. For example, here's a synchronized statement for writing the latest score in a game to a file:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir()
, TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
You should synchronize your read statements with the same lock.
Then, in your
BackupAgentHelper
, you must override
onBackup()
and
onRestore()
to synchronize the backup and restore operations with the same intrinsic lock. For example, the
MyFileBackupAgent
example from above needs the following methods:
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
// Hold the lock while the FileBackupHelper performs backup
synchronized (MyActivity.sDataLock) {
super.onBackup(oldState, data, newState);
}
}
@Override
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
// Hold the lock while the FileBackupHelper restores the file
synchronized (MyActivity.sDataLock) {
super.onRestore(data, appVersionCode, newState);
}
}
Checking the Restore Data Version
When the Backup Manager saves your data to cloud storage, it automatically includes the version of your application, as defined by your manifest file's
android:versionCode
attribute. Before the Backup Manager calls your backup agent to restore your data, it looks at the
android:versionCode
of the installed application and compares it to the value recorded in the restore data set. If the version recorded in the restore data set is
newer than the application version on the device, then the user has downgraded their application. In this case, the Backup Manager will abort the restore operation for your application and not call your
onRestore()
method, because the restore set is considered meaningless to an older version.
You can override this behavior with the
android:restoreAnyVersion
attribute. This attribute is either "
true
" or "
false
" to indicate whether you want to restore the application regardless of the restore set version. The default value is "
false
". If you define this to be "
true
" then the Backup Manager will ignore the
android:versionCode
and call your
onRestore()
method in all cases. In doing so, you can manually check for the version difference in your
onRestore()
method and take any steps necessary to make the data compatible if the versions conflict.
To help you handle different versions during a restore operation, the
onRestore()
method passes you the version code included with the restore data set as the
appVersionCode
parameter. You can then query the current application's version code with the
PackageInfo.versionCode
field. For example:
PackageInfo info;
try {
String name = getPackageName
();
info = getPackageManager
().getPackageInfo
(name,0);
} catch (NameNotFoundException nnfe) {
info = null;
}
int version;
if (info != null) {
version = info.versionCode;
}
Caution: Be certain you understand the consequences of setting
android:restoreAnyVersion
to "
true
" for your application. If each version of your application that supports backup does not properly account for variations in your data format during
onRestore()
, then the data on the device could be saved in a format incompatible with the version currently installed on the device.
Requesting Backup
You can request a backup operation at any time by calling
dataChanged()
. This method notifies the Backup Manager that you'd like to backup your data using your backup agent. The Backup Manager then calls your backup agent's
onBackup()
method at an opportune time in the future. Typically, you should request a backup each time your data changes (such as when the user changes an application preference that you'd like to back up). If you call
dataChanged()
several times consecutively, before the Backup Manager requests a backup from your agent, your agent still receives just one call to
onBackup()
.
Note: While developing your application, you can request a backup and initiate an immediate backup operation with the
bmgr
tool.
Requesting Restore
During the normal life of your application, you shouldn't need to request a restore operation. They system automatically checks for backup data and performs a restore when your application is installed. However, you can manually request a restore operation by calling
requestRestore()
, if necessary. In which case, the Backup Manager calls your
onRestore()
implementation, passing the data from the current set of backup data.
Note: While developing your application, you can request a restore operation with the
bmgr
tool.
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
.
- 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 Google Play built in.
- Ensure that backup is enabled
- 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
- Initiate a backup operation:
adb shell bmgr run
This forces the Backup Manager to perform all backup requests that are in its queue.
- Uninstall your application:
adb uninstall your.package.name
- Re-install your application.
If your backup agent is successful, all the data you initialized in step 4 is restored.